home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / Pascal / source / NIH Image V1.54 Source / Analysis.p next >
Encoding:
Text File  |  1994-01-31  |  72.0 KB  |  2,596 lines  |  [TEXT/PJMM]

  1. unit Analysis;
  2.  
  3. {Analysis routines used by the Image program}
  4.  
  5. interface
  6.  
  7.     uses
  8.         QuickDraw, Palettes, PrintTraps, globals, Utilities, LeastSquares, Graphics, file1, file2, Ellipse, Lut;
  9.  
  10.  
  11.  
  12.     procedure DoHistogram;
  13.     procedure GetRectHistogram;
  14.     function SetupMask: boolean;
  15.     procedure GetHistogram;
  16.     procedure ShowContinuousHistogram;
  17.     procedure ComputeResults;
  18.     procedure FindThresholdingMode;
  19.     procedure Measure;
  20.     procedure UpdateRoiLineWidth;
  21.     procedure DoProfilePlotOptions;
  22.     procedure ShowResults;
  23.     procedure PlotDensityProfile;
  24.     procedure SetScale;
  25.     procedure Calibrate;
  26.     procedure ResetCounter;
  27.     procedure DoMeasurementOptions;
  28.     procedure DoPoints (event: EventRecord);
  29.     procedure FindAngle (event: EventRecord);
  30.     procedure SaveBlankField;
  31.     procedure UndoLastMeasurement (DisplayResults: boolean);
  32.     procedure MarkSelection (count: integer);
  33.     procedure AutoOutline (start: point);
  34.     procedure RedoMeasurement;
  35.     procedure DeleteMeasurement;
  36.     procedure AnalyzeParticles;
  37.     procedure MakeNonStraightLineRoi (RoiKind: RoiTypeType);
  38.  
  39.  
  40. implementation
  41.  
  42.     const
  43.         UnitsPopUpID = 6;
  44.  
  45.     var
  46.         WandMode: (LUTMode, GrayMapMode, BinaryMode);
  47.         GrayMapThreshold: integer;
  48.         InfoForRedirect: InfoPtr;
  49.         UnitsKind: UnitsType;
  50.  
  51.  
  52. {$PUSH}
  53. {$D-}
  54.  
  55.  
  56.     procedure DoHistogramOfLine (data: ptr; var histogram: HistogramType; width: LongInt);
  57. {}
  58. {VAR}
  59. {  line:LinePtr;}
  60. {  i,value:integer;}
  61. {BEGIN}
  62. {  line:=LinePtr(data);}
  63. {  FOR i:=0 TO width-1 DO BEGIN}
  64. {    value:=line^[i];}
  65. {    histogram[value]:=histogram[value]+1;}
  66. {  END;}
  67. {}
  68.     {a0=data}
  69.     {a1=histogram}
  70.     {d0=width}
  71.     {d1=pixel value}
  72.     inline
  73.         $4E56, $0000, {  link a6,#0}
  74.         $48E7, $C0C0, {  movem.l a0-a1/d0-d1,-(sp)}
  75.         $206E, $000C, {  move.l 12(a6),a0}
  76.         $226E, $0008, {  move.l 8(a6),a1}
  77.         $202E, $0004, {  move.l 4(a6),d0}
  78.         $5380,       {  subq.l #1,d0}
  79.         $4281,       {L clr.l d1}
  80.         $1218,       {  move.b (a0)+,d1}
  81.         $E541,       {  asl.w #2,d1}
  82.         $52B1, $1800, {  addq.l #1,0(a1,d1.l)}
  83.         $51C8, $FFF4, {  dbra d0,L}
  84.         $4CDF, $0303, {  movem.l (sp)+,a0-a1/d0-d1}
  85.         $4E5E,       {  unlk a6}
  86.         $DEFC, $000C; {  add.w #12,sp}
  87. {END;}
  88.  
  89.  
  90.     procedure GetRectHistogram;
  91.         var
  92.             width, i, NumberOfLines: integer;
  93.             offset: LongInt;
  94.             p: ptr;
  95.     begin
  96.         if TooWide then
  97.             exit(GetRectHistogram);
  98.         ShowWatch;
  99.         for i := 0 to 255 do
  100.             Histogram[i] := 0;
  101.         with info^.RoiRect, info^ do begin
  102.                 offset := LongInt(top) * BytesPerRow + left;
  103.                 p := ptr(ord4(PicBaseAddr) + offset);
  104.                 width := right - left;
  105.                 NumberOfLines := bottom - top;
  106.             end;
  107.         if width > 0 then
  108.             for i := 1 to NumberOfLines do begin
  109.                     DoHistogramOfLine(p, histogram, width);
  110.                     p := ptr(ord4(p) + info^.BytesPerRow);
  111.                 end
  112.     end;
  113.  
  114.  
  115.     function SetupMask: boolean;
  116. {Creates a mask in the undo buffer for operating}
  117. {on non-rectangular selections .}
  118.         var
  119.             tPort: GrafPtr;
  120.             SaveInfo: InfoPtr;
  121.     begin
  122.         if NoUndo then begin
  123.                 SetupMask := false;
  124.                 exit(SetupMask)
  125.             end;
  126.         SetupUndoInfoRec;
  127.         SaveInfo := Info;
  128.         Info := UndoInfo;
  129.         GetPort(tPort);
  130.         with Info^ do begin
  131.                 SetPort(GrafPtr(osPort));
  132.                 pmForeColor(BlackIndex);
  133.                 pmBackColor(WhiteIndex);
  134.                 PenNormal;
  135.                 EraseRect(RoiRect);
  136.                 PaintRgn(roiRgn);
  137.             end;
  138.         SetPort(tPort);
  139.         Info := SaveInfo;
  140.         SetupMask := true;
  141.     end;
  142.  
  143.  
  144.     procedure SetupRedirectedSampling;
  145.         var
  146.             info1, info2: InfoPtr;
  147.     begin
  148.         InfoForRedirect := nil;
  149.         if nPics <> 2 then begin
  150.                 PutMessage('There must be exactly two image windows open to do redirected sampling.');
  151.                 AnalyzingParticles := false;
  152.                 exit(SetupRedirectedSampling);
  153.             end;
  154.         Info1 := pointer(WindowPeek(PicWindow[1])^.RefCon);
  155.         Info2 := pointer(WindowPeek(PicWindow[2])^.RefCon);
  156.         if not EqualRect(info1^.PicRect, info2^.PicRect) then begin
  157.                 PutMessage('The image windows must be exactly the same size to do redirected sampling.');
  158.                 AnalyzingParticles := false;
  159.                 exit(SetupRedirectedSampling);
  160.             end;
  161.         if info = info1 then
  162.             InfoForRedirect := info2
  163.         else
  164.             InfoForRedirect := info1;
  165.     end;
  166.  
  167.  
  168.     procedure GetHistogram;
  169.         var
  170.             MaskLine, DataLine: LineType;
  171.             width, i, vloc: integer;
  172.             sum, sum2, count, OverFlows: LongInt;
  173.             SaveInfo: InfoPtr;
  174.             value: LongInt;
  175.             trect: rect;
  176.     begin
  177.         if TooWide then
  178.             exit(GetHistogram);
  179.         ShowWatch;
  180.         if RedirectSampling then
  181.             SetupRedirectedSampling
  182.         else
  183.             InfoForRedirect := nil;
  184.         if not SetupMask then
  185.             beep;
  186.         SaveInfo := Info;
  187.         for i := 0 to 255 do
  188.             Histogram[i] := 0;
  189.         if FitEllipse then
  190.             ResetSums;
  191.         trect := info^.RoiRect;
  192.         with trect do begin
  193.                 width := right - left;
  194.                 for vloc := top to bottom - 1 do begin
  195.                         if InfoForRedirect <> nil then
  196.                             Info := InfoForRedirect
  197.                         else
  198.                             Info := SaveInfo;
  199.                         GetLine(left, vloc, width, DataLine);
  200.                         Info := UndoInfo;
  201.                         GetLine(left, vloc, width, MaskLine);
  202.                         if FitEllipse then
  203.                             ComputeSums(vloc - top, width, MaskLine);
  204.                         for i := 0 to width - 1 do
  205.                             if MaskLine[i] = BlackIndex then begin
  206.                                     value := DataLine[i];
  207.                                     histogram[value] := histogram[value] + 1;
  208.                                 end;
  209.                     end;
  210.             end;
  211.         Info := SaveInfo;
  212.         if not AnalyzingParticles then
  213.             SetupUndo; {Needed for drawing "marching ants".}
  214.     end;
  215.  
  216.  
  217.     procedure ComputeResults;
  218.         var
  219.             MaxCount, icount, isum, n: LongInt;
  220.             i: integer;
  221.             sum, sum2, ri, rcount, tSD, rmode, xc, yc: extended;
  222.             Major, Minor, EllipseAngle, hcenter, vcenter, calValue: extended;
  223.             MinCalibratedValue, MaxCalibratedValue, CalibratedMean: extended;
  224.             IgnoreThresholding: boolean;
  225.             ulength, clength: real;
  226.     begin
  227.         with info^, results do begin
  228.                 case ThresholdingMode of
  229.                     DensitySlice:  begin
  230.                             MinIndex := SliceStart;
  231.                             MaxIndex := SliceEnd;
  232.                         end;
  233.                     GrayMapThresholding:  begin
  234.                             MinIndex := GrayMapThreshold;
  235.                             MaxIndex := 255;
  236.                         end;
  237.                     BinaryImage:  begin
  238.                             MinIndex := BlackIndex;
  239.                             MaxIndex := BlackIndex;
  240.                         end;
  241.                     NoThresholding:  begin
  242.                             MinIndex := 0;
  243.                             MaxIndex := 255;
  244.                         end;
  245.                 end;
  246.                 IgnoreThresholding := RedirectSampling or (IncludeHoles and (AnalyzingParticles or (CurrentTool = Wand)));
  247.                 if IgnoreThresholding then begin
  248.                         MinIndex := 0;
  249.                         MaxIndex := 255;
  250.                     end;
  251.                 while (histogram[MinIndex] = 0) and (MinIndex < 255) do
  252.                     MinIndex := MinIndex + 1;
  253.                 while (histogram[MaxIndex] = 0) and (MaxIndex > 0) do
  254.                     MaxIndex := MaxIndex - 1;
  255.                 MaxCount := 0;
  256.                 sum := 0.0;
  257.                 isum := 0;
  258.                 sum2 := 0.0;
  259.                 n := 0;
  260.                 minCalibratedValue := 10e100;
  261.                 maxCalibratedValue := -10e100;
  262.                 rmode := 0.0;
  263.                 imode := 0;
  264.                 for i := MinIndex to MaxIndex do begin
  265.                         calValue := cvalue[i];
  266.                         icount := histogram[i];
  267.                         rcount := icount;
  268.                         sum := sum + rcount * calValue;
  269.                         isum := isum + icount * i;
  270.                         ri := i;
  271.                         sum2 := sum2 + sqr(calValue) * rcount;
  272.                         n := n + icount;
  273.                         if icount > MaxCount then begin
  274.                                 MaxCount := icount;
  275.                                 rmode := cvalue[i];
  276.                                 imode := i
  277.                             end;
  278.                         if calValue < minCalibratedValue then
  279.                             minCalibratedValue := calValue;
  280.                         if calValue > maxCalibratedValue then
  281.                             maxCalibratedValue := calValue;
  282.                     end;
  283.                 if ContinuousHistoGram then
  284.                     exit(ComputeResults);
  285.                 if n = 0 then begin
  286.                         minCalibratedValue := 0.0;
  287.                         maxCalibratedValue := 0.0;
  288.                     end;
  289.                 if n > 0 then begin
  290.                         CalibratedMean := sum / n;
  291.                         UncalibratedMean := isum / n
  292.                     end
  293.                 else begin
  294.                         CalibratedMean := 0.0;
  295.                         UncalibratedMean := 0.0
  296.                     end;
  297.                 IncrementCounter;
  298.                 mean^[mCount] := CalibratedMean;
  299.                 mMin^[mCount] := minCalibratedValue;
  300.                 mMax^[mCount] := maxCalibratedValue;
  301.                 if mCount <= MaxStandards then
  302.                     umean[mCount] := UncalibratedMean;
  303.                 if (n > 0) and (CalibratedMean > 0.0) then begin
  304.                         rcount := n;
  305.                         tSD := (rcount * Sum2 - sqr(sum)) / rcount;
  306.                         if tSD > 0.0 then
  307.                             tSD := sqrt(tSD / (rcount - 1.0))
  308.                         else
  309.                             tSD := 0.0
  310.                     end
  311.                 else
  312.                     tSD := 0.0;
  313.                 sd^[mCount] := tSD;
  314.                 with info^.RoiRect do begin
  315.                         xc := left + (right - left) / 2;
  316.                         yc := top + (bottom - top) / 2;
  317.                         if InvertYCoordinates then
  318.                             yc := PicRect.bottom - yc;
  319.                         if SpatiallyCalibrated then begin
  320.                                 xc := xc / xSpatialScale;
  321.                                 yc := yc / ySpatialScale;
  322.                             end;
  323.                         xcenter^[mCount] := xc;
  324.                         ycenter^[mCount] := yc;
  325.                     end;
  326.                 PixelCount^[mCount] := n;
  327.                 ulength := 0.0;
  328.                 clength := 0.0;
  329.                 with RoiRect do
  330.                     case RoiType of
  331.                         RectRoi:  begin
  332.                                 uLength := ((right - left) + (bottom - top)) * 2.0;
  333.                                 cLength := uLength;
  334.                                 if SpatiallyCalibrated then
  335.                                     cLength := ((right - left) / xSpatialScale + (bottom - top) / ySpatialScale) * 2.0;
  336.                             end;
  337.                         OvalRoi:  begin
  338.                                 uLength := pi * ((right - left) + (bottom - top)) / 2.0;
  339.                                 cLength := uLength;
  340.                                 if SpatiallyCalibrated then
  341.                                     cLength := pi * ((right - left) / xSpatialScale + (bottom - top) / ySpatialScale) / 2.0;
  342.                             end;
  343.                         LineRoi, SegLineRoi, FreeLineRoi:  begin
  344.                                 GetLengthOrPerimeter(ulength, clength);
  345.                                 nLengths := nLengths + 1;
  346.                             end;
  347.                         PolygonRoi, FreehandRoi: 
  348.                             if (LengthM in Measurements) or (nLengths > 0) or WandAdjustAreas then
  349.                                 GetLengthOrPerimeter(ulength, clength);
  350.                         otherwise
  351.                     end;
  352.                 if SpatiallyCalibrated then
  353.                     plength^[mCount] := cLength
  354.                 else
  355.                     plength^[mcount] := uLength;
  356.                 if SpatiallyCalibrated then
  357.                     mArea^[mCount] := n / (xSpatialScale * ySpatialScale)
  358.                 else
  359.                     mArea^[mCount] := n;
  360.                 mode^[mCount] := rmode;
  361.                 if FitEllipse and ((RoiType = PolygonRoi) or (RoiType = FreehandRoi) or (RoiType = LineRoi) or (RoiType = FreeLineRoi) or (RoiType = SegLineRoi)) then begin
  362.                         GetEllipseParam(Major, Minor, EllipseAngle, xc, yc);
  363.                         if InvertYCoordinates then
  364.                             yc := PicRect.bottom - yc;
  365.                         if SpatiallyCalibrated then begin
  366.                                 Major := Major / xSpatialScale;
  367.                                 Minor := Minor / ySpatialScale;
  368.                                 xc := xc / xSpatialScale;
  369.                                 yc := yc / ySpatialScale;
  370.                             end;
  371.                         MajorAxis^[mCount] := Major * 2.0;
  372.                         MinorAxis^[mCount] := Minor * 2.0;
  373.                         orientation^[mCount] := EllipseAngle;
  374.                         xcenter^[mCount] := xc;
  375.                         ycenter^[mCount] := yc;
  376.                     end
  377.                 else if RoiType = OvalRoi then
  378.                     with RoiRect do begin
  379.                             Major := right - left;
  380.                             Minor := bottom - top;
  381.                             if SpatiallyCalibrated then begin
  382.                                     Major := Major / xSpatialScale;
  383.                                     Minor := Minor / ySpatialScale;
  384.                                 end;
  385.                             MajorAxis^[mCount] := Major;
  386.                             MinorAxis^[mCount] := Minor;
  387.                             orientation^[mCount] := 0.0;
  388.                         end
  389.                 else begin
  390.                         MajorAxis^[mCount] := 0.0;
  391.                         MinorAxis^[mCount] := 0.0;
  392.                         orientation^[mCount] := 0.0;
  393.                     end;
  394.             end; {with}
  395.         measuring := true;
  396.         ValuesMessage := '';
  397.     end;
  398.  
  399.  
  400.     procedure FindThresholdingMode;
  401.     begin
  402.         with info^ do begin
  403.                 if DensitySlicing then
  404.                     ThresholdingMode := DensitySlice
  405.                 else if thresholding then begin
  406.                         ThresholdingMode := GrayMapThresholding;
  407.                         GrayMapThreshold := ColorStart;
  408.                         if GrayMapThreshold < 0 then
  409.                             GrayMapThreshold := 0;
  410.                         if GrayMapThreshold > 255 then
  411.                             GrayMapThreshold := 255;
  412.                     end
  413.                 else if BinaryPic then
  414.                     ThresholdingMode := BinaryImage
  415.                 else
  416.                     ThresholdingMode := NoThresholding;
  417.             end;
  418.     end;
  419.  
  420.  
  421.     procedure Measure;
  422.         var
  423.             AutoSelectAll: boolean;
  424.             SaveN: integer;
  425.     begin
  426.         if NotInBounds then
  427.             exit(Measure);
  428.         with info^ do begin
  429.                 FindThresholdingMode;
  430.                 if ThresholdingMode = BinaryImage then
  431.                     ThresholdingMode := NoThresholding;
  432.                 AutoSelectAll := not RoiShowing;
  433.                 if AutoSelectAll then
  434.                     SelectAll(false);
  435.                 if (RoiType = RectRoi) and (not RedirectSampling) then
  436.                     GetRectHistogram
  437.                 else
  438.                     GetHistogram;
  439.                 if MeasurementToRedo > 0 then begin
  440.                         SaveN := mCount;
  441.                         mCount := MeasurementToRedo - 1;
  442.                         ComputeResults;
  443.                         ShowValues;
  444.                         mCount := SaveN;
  445.                         MeasurementToRedo := 0;
  446.                         UpdateList;
  447.                     end
  448.                 else begin
  449.                         ComputeResults;
  450.                         ShowValues;
  451.                         AppendResults;
  452.                         if RoiType = LineRoi then
  453.                             if nLengths = 1 then
  454.                                 if not (LengthM in Measurements) then
  455.                                     UpdateList;
  456.                     end;
  457.                 RoiShowing := true;
  458.                 WhatToUndo := UndoMeasurement;
  459.                 if AutoSelectAll then
  460.                     KillRoi;
  461.                 UpdateScreen(OldRoiRect);
  462.             end;
  463.     end;
  464.  
  465.  
  466.     procedure ShowHistogram;
  467.         var
  468.             htop: integer;
  469.             tport: GrafPtr;
  470.             hrect, prect, srect: rect;
  471.             FirstTime: boolean;
  472.     begin
  473.         GetPort(tPort);
  474.         FirstTime := HistoWindow = nil;
  475.         if FirstTime then begin
  476.                 htop := ScreenHeight - hheight - 10;
  477.                 SetRect(HistoRect, hleft, htop, hleft + hwidth, htop + hheight);
  478.                 HistoWindow := NewWindow(nil, HistoRect, 'Histogram', true, NoGrowDocProc, nil, true, 0);
  479.                 WindowPeek(HistoWindow)^.WindowKind := HistoKind;
  480.             end;
  481.         if FirstTime or (VideoControl = nil) then
  482.             SelectWindow(HistoWindow);
  483.         SetPort(HistoWindow);
  484.         InvalRect(HistoWindow^.PortRect);
  485.         SetPort(tPort);
  486.     end;
  487.  
  488.  
  489.     procedure ShowContinuousHistogram;
  490.         const
  491.             skip = 10;
  492.         var
  493.             i, NumberOfLines: integer;
  494.             offset: LongInt;
  495.             p: ptr;
  496.     begin
  497.         for i := 0 to 255 do
  498.             Histogram[i] := 0;
  499.         p := ptr(ptr(fgSlotBase));
  500.         NumberOfLines := ((fgHeight) div skip) - 1;
  501.         offset := fgRowBytes * skip;
  502.         for i := 1 to NumberOfLines do begin
  503.                 DoHistogramOfLine(p, histogram, fgWidth);
  504.                 p := ptr(ord4(p) + offset);
  505.             end;
  506.         ThresholdingMode := NoThresholding;
  507.         HistogramSliceStart := 0;
  508.         HistogramSliceEnd := 255;
  509.         ComputeResults;
  510.         ShowHistogram;
  511.     end;
  512.  
  513.  
  514.     procedure DoHistogram;
  515.         var
  516.             AutoSelectAll: boolean;
  517.     begin
  518.         if NotInBounds then
  519.             exit(DoHistogram);
  520.         if digitizing then begin
  521.                 if ContinuousHistogram then
  522.                     ContinuousHistogram := false
  523.                 else begin
  524.                         ContinuousHistogram := true;
  525.                         if info <> NoInfo then
  526.                             with info^ do begin
  527.                                     RoiType := NoRoi;
  528.                                     RoiRect := SrcRect;
  529.                                 end;
  530.                     end;
  531.                 exit(DoHistogram)
  532.             end;
  533.         AutoSelectAll := not info^.RoiShowing;
  534.         if AutoSelectAll then
  535.             SelectAll(false);
  536.         if (info^.RoiType = RectRoi) and (not RedirectSampling) then
  537.             GetRectHistogram
  538.         else
  539.             GetHistogram;
  540.         ThresholdingMode := NoThresholding;
  541.         ComputeResults;
  542.         ShowCount := false;
  543.         ShowValues;
  544.         ShowCount := true;
  545.         FindThresholdingMode;
  546.         case ThresholdingMode of
  547.             DensitySlice:  begin
  548.                     HistogramSliceStart := SliceStart;
  549.                     HistogramSliceEnd := SliceEnd;
  550.                 end;
  551.             GrayMapThresholding:  begin
  552.                     HistogramSliceStart := GrayMapThreshold;
  553.                     HistogramSliceEnd := 255;
  554.                 end;
  555.             BinaryImage, NoThresholding:  begin
  556.                     HistogramSliceStart := 0;
  557.                     HistogramSliceEnd := 255;
  558.                 end;
  559.         end;
  560.         ShowHistogram;
  561.         UndoLastMeasurement(false);
  562.         WhatToUndo := NothingToUndo;
  563.         if AutoSelectAll then
  564.             KillRoi;
  565.     end;
  566.  
  567.  
  568. {$POP}
  569.  
  570.     procedure PlotDensityProfile;
  571.         var
  572.             hloc, vloc, value, width, height, i: integer;
  573.             aLine: LineType;
  574.             sum: array[0..MaxLine] of real;
  575.             start, p1, p2: point;
  576.     begin
  577.         with info^ do
  578.             if RoiShowing then
  579.                 case RoiType of
  580.                     LineRoi:  begin
  581.                             PlotLineProfile;
  582.                             exit(PlotDensityProfile);
  583.                         end;
  584.                     FreeLineRoi, SegLineRoi, PolygonRoi, FreehandRoi:  begin
  585.                             PlotArbitraryLine;
  586.                             exit(PlotDensityProfile);
  587.                         end;
  588.                 end; {case}
  589.         if NoSelection or NotRectangular or NotInBounds then
  590.             exit(PlotDensityProfile);
  591.         ShowWatch;
  592.         with info^.RoiRect do begin
  593.                 width := right - left;
  594.                 height := bottom - top;
  595.                 start.h := left;
  596.                 start.v := bottom;
  597.                 if (width >= height) or (OptionKeyWasDown) then begin
  598.             {Column Average Plot}
  599.                         if width > MaxLine then
  600.                             width := MaxLine;
  601.                         for i := 0 to width - 1 do
  602.                             sum[i] := 0.0;
  603.                         for vloc := top to bottom - 1 do begin
  604.                                 GetLine(left, vloc, width, aLine);
  605.                                 for i := 0 to width - 1 do
  606.                                     sum[i] := sum[i] + cvalue[aLine[i]];
  607.                             end;
  608.                         for i := 0 to width - 1 do
  609.                             PlotData^[i] := sum[i] / height;
  610.                         PlotCount := width;
  611.                         PlotAvg := height;
  612.                         PlotStart.h := left;
  613.                         PlotStart.v := top + (bottom - top) div 2;
  614.                         PlotAngle := 0.0;
  615.                         ComputePlotMinAndMax;
  616.                         if ShowPlot then
  617.                             SetupPlot(start, false);
  618.                     end
  619.                 else begin
  620.            {Row Average Plot}
  621.                         if height > MaxLine then
  622.                             height := MaxLine;
  623.                         for i := 0 to height - 1 do
  624.                             sum[i] := 0.0;
  625.                         for hloc := left to right - 1 do begin
  626.                                 GetColumn(hloc, top, height, aLine);
  627.                                 for i := 0 to height - 1 do
  628.                                     sum[i] := sum[i] + cValue[aLine[i]];
  629.                             end;
  630.                         for i := 0 to height - 1 do
  631.                             PlotData^[i] := sum[i] / width;
  632.                         PlotCount := height;
  633.                         PlotAvg := width;
  634.                         PlotStart.h := left + (right - left) div 2;
  635.                         PlotStart.v := top;
  636.                         PlotAngle := 270.0;
  637.                         ComputePlotMinAndMax;
  638.                         if ShowPlot then
  639.                             SetupPlot(start, true);
  640.                     end;
  641.             end; {with}
  642.     end;
  643.  
  644.  
  645.     procedure SetScaleUProc (d: DialogPtr; item: integer);
  646.      {User proc for Set Scale dialog box}
  647.         var
  648.             str: str255;
  649.             VersInfo: str255;
  650.             r: rect;
  651.     begin
  652.         SetPort(d);
  653.         GetDItemRect(d, item, r);
  654.         DrawDropBox(r);
  655.         GetItem(UnitsMenuH, ord(UnitsKind) + 1, str);
  656.         DrawPopUpText(str, r);
  657.     end;
  658.  
  659.  
  660.     procedure SetScale;
  661.         const
  662.             MeasuredDistanceID = 3;
  663.             KnownDistanceID = 4;
  664.             AspectRatioID = 5;
  665.             ScaleID = 7;
  666.             UnitsTextID = 8;
  667.         var
  668.             mylog: DialogPtr;
  669.             item, i: integer;
  670.             SaveUnitsKind, OldUnitsKind, MenuUnitsKind: UnitsType;
  671.             KnownDistance, MeasuredDistance, SaveScale, TempScale, CalibratedDistance: double;
  672.             UnitsPerCM, OldUnitsPerCM, SaveRawScale, SaveAspectRatio: double;
  673.             ignore, MenuItem: integer;
  674.             str: str255;
  675.             SaveUnits: UnitType;
  676.             isLineSelection: boolean;
  677.             ulength, clength: real;
  678.             r: rect;
  679.     begin
  680.         with info^ do begin
  681.                 if (not RoiShowing) and (CurrentTool = LineTool) and (NoInfo^.roiType = LineRoi) then
  682.                     RestoreRoi;
  683.                 isLineSelection := RoiShowing and (RoiType = LineRoi);
  684.                 InitCursor;
  685.                 if isLineSelection then begin
  686.                         GetLengthOrPerimeter(ulength, clength);
  687.                         MeasuredDistance := ulength;
  688.                     end
  689.                 else
  690.                     MeasuredDistance := 0.0;
  691.                 if not SpatiallyCalibrated then
  692.                     xUnit := 'pixel';
  693.                 GetUnitsKind(UnitsKind, UnitsPerCM);
  694.                 SaveUnits := xUnit;
  695.                 SaveUnitsKind := UnitsKind;
  696.                 SaveScale := xSpatialScale;
  697.                 SaveAspectRatio := PixelAspectRatio;
  698.                 KnownDistance := 0.0;
  699.                 mylog := GetNewDialog(10, nil, pointer(-1));
  700.                 SetDReal(MyLog, MeasuredDistanceID, MeasuredDistance, 2);
  701.                 SetDReal(MyLog, KnownDistanceID, KnownDistance, 2);
  702.                 SelIText(MyLog, KnownDistanceID, 0, 32767);
  703.                 SetDReal(MyLog, AspectRatioID, PixelAspectRatio, 4);
  704.                 SetUProc(myLog, UnitsPopupID, @SetScaleUProc);
  705.                 if UnitsKind = pixels then
  706.                     TempScale := 1.0
  707.                 else
  708.                     TempScale := xSpatialScale;
  709.                 if trunc(TempScale) = TempScale then
  710.                     SetDReal(MyLog, ScaleID, TempScale, 0)
  711.                 else
  712.                     SetDReal(MyLog, ScaleID, TempScale, 5);
  713.                 SetDString(MyLog, UnitsTextID, xUnit);
  714.                 setport(myLog);
  715.                 repeat
  716.                     ModalDialog(nil, item);
  717.                     if item = MeasuredDistanceID then
  718.                         MeasuredDistance := GetDReal(MyLog, MeasuredDistanceID);
  719.                     if item = KnownDistanceID then
  720.                         KnownDistance := GetDReal(MyLog, KnownDistanceID);
  721.                     if item = ScaleID then begin
  722.                             MeasuredDistance := GetDReal(MyLog, ScaleID);
  723.                             KnownDistance := 1;
  724.                             SetDReal(MyLog, MeasuredDistanceID, MeasuredDistance, 2);
  725.                             SetDReal(MyLog, KnownDistanceID, KnownDistance, 2);
  726.                         end;
  727.                     if item = AspectRatioID then begin
  728.                             PixelAspectRatio := GetDReal(MyLog, AspectRatioID);
  729.                             if PixelAspectRatio <= 0.0 then begin
  730.                                     beep;
  731.                                     PixelAspectRatio := 1.0;
  732.                                 end
  733.                             else
  734.                                 ySpatialScale := xSpatialScale / PixelAspectRatio;
  735.                         end;
  736.                     if item = UnitsPopUpID then begin
  737.                             OldUnitsKind := UnitsKind;
  738.                             OldUnitsPerCM := UnitsPerCM;
  739.                             GetDItemRect(myLog, item, r);
  740.                             InvertRect(r);
  741.                             MenuItem := PopUpMenu(UnitsMenuH, r.left, r.top, ord(UnitsKind) + 1);
  742.                             InvertRect(r);
  743.                             GetItem(UnitsMenuH, MenuItem, str);
  744.                             DrawPopUpText(str, r);
  745.                             UnitsKind := UnitsType(MenuItem - 1);
  746.                             GetXUnits(UnitsKind);
  747.                             if (UnitsType(MenuItem - 1) = OtherUnits) and (OldUnitsKind <> OtherUnits) then
  748.                                 xUnit := 'unit';
  749.                             SetDString(MyLog, UnitsTextID, xUnit);
  750.                             GetUnitsKind(UnitsKind, UnitsPerCM);
  751.                             if (UnitsPerCM <> OldUnitsPerCM) and (UnitsPerCM <> 0.0) and (OldUnitsPerCM <> 0.0) then begin
  752.                                     xSpatialScale := xSpatialScale * (OldUnitsPerCM / UnitsPerCM);
  753.                                     ySpatialScale := xSpatialScale / PixelAspectRatio;
  754.                                 end;
  755.                             if UnitsKind = Pixels then
  756.                                 KnownDistance := 0.0;
  757.                         end;
  758.                     if (item = KnownDistanceID) or (item = MeasuredDistanceID) or (item = ScaleID) then
  759.                         if (UnitsKind = Pixels) and (item <> cancel) then
  760.                             PutMessage('Please select a measurent unit (not pixels) before setting or changing the scale.')
  761.                         else begin
  762.                                 if (MeasuredDistance > 0.0) and (KnownDistance > 0.0) then begin
  763.                                         xSpatialScale := MeasuredDistance / KnownDistance;
  764.                                         ySpatialScale := xSpatialScale / PixelAspectRatio;
  765.                                     end;
  766.                             end;
  767.                     if UnitsKind = pixels then
  768.                         TempScale := 1.0
  769.                     else
  770.                         TempScale := xSpatialScale;
  771.                     if item <> ScaleID then begin
  772.                             if (trunc(TempScale) = TempScale) or (TempScale >= 10000.0) then
  773.                                 SetDReal(MyLog, ScaleID, TempScale, 0)
  774.                             else if TempScale < 0.01 then
  775.                                 SetDReal(MyLog, ScaleID, TempScale, 6)
  776.                             else
  777.                                 SetDReal(MyLog, ScaleID, TempScale, 3);
  778.                         end;
  779.                     if item = UnitsTextID then begin
  780.                             xUnit := GetDString(myLog, item);
  781.                             GetUnitsKind(UnitsKind, UnitsPerCM);
  782.                             GetDItemRect(myLog, UnitsPopUpID, r);
  783.                             InvalRect(r);
  784.                         end;
  785.                 until (item = ok) or (item = cancel);
  786.                 DisposDialog(mylog);
  787.                 if item = cancel then begin
  788.                         xUnit := SaveUnits;
  789.                         UnitsKind := SaveUnitsKind;
  790.                         xSpatialScale := SaveScale;
  791.                         PixelAspectRatio := SaveAspectRatio;
  792.                     end
  793.                 else
  794.                     Changes := true;
  795.                 SpatiallyCalibrated := (xSpatialScale <> 0.0) and (xUnit <> 'pixel');
  796.                 if not SpatiallyCalibrated then begin
  797.                         UnitsKind := Pixels;
  798.                         UnitsPerCm := 0.0;
  799.                     end;
  800.                 UpdateTitleBar;
  801.             end; {with info^}
  802.     end;
  803.  
  804.  
  805. {$PUSH}
  806. {$D-}
  807.  
  808.  
  809.     procedure SetupCalibrationPlot;
  810.         const
  811.             hrange = 1024;
  812.             hmax = 1023;
  813.             vrange = 600;
  814.             vmax = 599;
  815.             SymbolSize = 11;
  816.         var
  817.             fRect, tRect: rect;
  818.             svalue, range, hscale, vscale, MinV, MaxV: extended;
  819.             tPort: GrafPtr;
  820.             i, hloc, vloc: integer;
  821.             SaveClipRegion: RgnHandle;
  822.             pt: point;
  823.     begin
  824.         PlotLeftMargin := 60;
  825.         PlotTopMargin := 15;
  826.         PlotBottomMargin := 30;
  827.         PlotRightMargin := 100;
  828.         MinV := MinValue;
  829.         MaxV := MaxValue;
  830.         for i := 1 to nStandards do begin
  831.                 svalue := StandardValues[i];
  832.                 if svalue < MinV then
  833.                     MinV := svalue;
  834.                 if svalue > MaxV then
  835.                     MaxV := svalue;
  836.             end;
  837.         range := MaxV - MinV;
  838.         PlotWidth := hrange div 3 + PlotLeftMargin + PlotRightMargin;
  839.         PlotHeight := vrange div 3 + PlotTopMargin + PlotBottomMargin;
  840.         PlotLeft := 64;
  841.         PlotTop := 64;
  842.         PlotCount := 256;
  843.         MakePlotWindow(PlotLeft, PlotTop, PlotWidth, PlotHeight);
  844.         if PlotWindow = nil then
  845.             exit(SetupCalibrationPlot);
  846.         WindowPeek(PlotWindow)^.WindowKind := CalibrationPlotKind;
  847.         SetRect(fRect, -SymbolSize, -SymbolSize, hmax + SymbolSize, vmax + SymbolSize);
  848.         GetPort(tPort);
  849.         SetPort(PlotWindow);
  850.         SaveClipRegion := PlotWindow^.ClipRgn;
  851.         RectRgn(PlotWindow^.ClipRgn, fRect);
  852.         hscale := 256 / hrange;
  853.         vscale := range / vrange;
  854.         PlotPICT := OpenPicture(fRect);
  855.         for i := 1 to nStandards do begin
  856.                 hloc := round(umean[i] / hscale);
  857.                 vloc := vmax - round((StandardValues[i] - MinValue) / vscale);
  858.                 SetRect(tRect, hloc - SymbolSize, vloc - SymbolSize, hloc + SymbolSize, vloc + SymbolSize);
  859.                 FrameOval(tRect);
  860.             end;
  861.         MoveTo(0, vmax - round((cvalue[0] - MinValue) / vscale));
  862.         for i := 1 to 255 do begin
  863.                 hloc := round(i / hscale);
  864.                 vloc := vmax - round((cvalue[i] - MinValue) / vscale);
  865.                 LineTo(hloc, vloc);
  866.             end;
  867.         ClosePicture;
  868.         PlotWindow^.ClipRgn := SaveClipRegion;
  869.         InvalRect(PlotWindow^.PortRect);
  870.         SetPort(tPort);
  871.         SelectWindow(PlotWindow);
  872.     end;
  873.  
  874.  
  875.     procedure DoCurveFitting;
  876.         var
  877.             i: integer;
  878.             XData, YData, YFit, Residuals, TempData: ColumnVector;
  879.             Variance: extended;
  880.             SumResidualsSqr, SumStandards, mean, SumMeanDiffSqr, DegreesOfFreedom: extended;
  881.             str1, str2: str255;
  882.     begin
  883.         with info^ do begin
  884.                 ShowWatch;
  885.                 if fit = RodbardFit then { need to reverse x and y to fit Rodbard equation }
  886.                     for i := 1 to nStandards do begin
  887.                             XData[i] := StandardValues[i];
  888.                             YData[i] := umean[i];
  889.                         end
  890.                 else
  891.                     for i := 1 to nStandards do begin
  892.                             XData[i] := umean[i];
  893.                             YData[i] := StandardValues[i];
  894.                         end;
  895.                 case fit of
  896.                     StraightLine: 
  897.                         nCoefficients := 2;
  898.                     Poly2: 
  899.                         nCoefficients := 3;
  900.                     Poly3: 
  901.                         nCoefficients := 4;
  902.                     Poly4: 
  903.                         nCoefficients := 5;
  904.                     Poly5: 
  905.                         nCoefficients := 6;
  906.                     ExpoFit: 
  907.                         nCoefficients := 2;
  908.                     PowerFit: 
  909.                         nCoefficients := 2;
  910.                     LogFit: 
  911.                         nCoefficients := 2;
  912.                     RodbardFit: 
  913.                         nCoefficients := 4;
  914.                 end;
  915.                 DegreesOfFreedom := nStandards - nCoefficients;
  916.                 if DegreesOfFreedom < 0 then begin
  917.                         FitGoodness := 0.0;
  918.                         DensityCalibrated := false;
  919.                         NumToString(nCoefficients, str1);
  920.                         case fit of
  921.                             StraightLine: 
  922.                                 str2 := 'straight line';
  923.                             Poly2: 
  924.                                 str2 := '2nd degree polynomial';
  925.                             Poly3: 
  926.                                 str2 := '3rd degree polynomial';
  927.                             Poly4: 
  928.                                 str2 := '4th degree polynomial';
  929.                             Poly5: 
  930.                                 str2 := '5th degree polynomial';
  931.                             ExpoFit: 
  932.                                 str2 := 'exponential';
  933.                             PowerFit: 
  934.                                 str2 := 'power';
  935.                             LogFit: 
  936.                                 str2 := 'log';
  937.                             RodbardFit: 
  938.                                 str2 := 'Rodbard';
  939.                         end;
  940.                         str2 := concat(' standards to do ', str2, ' fitting.');
  941.                         PutMessage(concat('You need at least ', str1, str2));
  942.                         exit(DoCurveFitting)
  943.                     end;
  944.                 DoSimplexFit(nStandards, nCoefficients, XData, YData, Coefficient, residuals);
  945.                 DensityCalibrated := true;
  946.                 ZeroClip := true;
  947.                 for i := 1 to nStandards do
  948.                     if ydata[i] < 0.0 then
  949.                         ZeroClip := false;
  950.                 GenerateValues;
  951.                 SumResidualsSqr := 0.0;
  952.                 SumStandards := 0.0;
  953.                 if fit = RodbardFit then
  954.                     for i := 1 to nStandards do begin
  955.                             tempdata[i] := StandardValues[i];
  956.                             StandardValues[i] := umean[i];
  957.                         end;
  958.                 for i := 1 to nStandards do begin
  959.                         SumResidualsSqr := SumResidualsSqr + sqr(residuals[i]);
  960.                         SumStandards := SumStandards + StandardValues[i];
  961.                     end;
  962.                 FitSD := Sqrt(SumResidualsSqr / nStandards);
  963.                 mean := SumStandards / nStandards;
  964.                 SumMeanDiffSqr := 0.0;
  965.                 for i := 1 to nStandards do
  966.                     SumMeanDiffSqr := SumMeanDiffSqr + sqr(StandardValues[i] - Mean);
  967.                 if (SumMeanDiffSqr > 0.0) and (DegreesOfFreedom <> 0) then
  968.                     FitGoodness := 1 - (SumResidualsSqr / DegreesOfFreedom) * ((nStandards - 1) / SumMeanDiffSqr)
  969.                 else
  970.                     FitGoodness := 1.0;
  971.                 if fit = RodbardFit then
  972.                     for i := 1 to nStandards do
  973.                         StandardValues[i] := tempdata[i];
  974.             end;
  975.         info^.changes := true;
  976.     end;
  977.  
  978.  
  979.     procedure GetStandardsFromFile (mylog: DialogPtr; FirstLevelID, FirstStandardID: integer);
  980.         var
  981.             fname, str: str255;
  982.             RefNum, i, nColumns, nValues: integer;
  983.             rLine: RealLine;
  984.     begin
  985.         RefNum := 0;
  986.         if not GetTextFile(fname, RefNum) then
  987.             exit(GetStandardsFromFile);
  988.         InitTextInput(fname, RefNum);
  989.         GetLineFromText(rLine, nValues);
  990.         if nValues = 1 then
  991.             nColumns := 1
  992.         else
  993.             nColumns := 2;
  994.         if (nStandards = 0) and (nColumns = 2) then begin
  995.                 i := 0;
  996.                 repeat
  997.                     i := i + 1;
  998.                     if i > MaxStandards then
  999.                         i := MaxStandards;
  1000.                     umean[i] := rLine[1];
  1001.                     SetDReal(MyLog, FirstLevelID + i - 1, umean[i], 2);
  1002.                     StandardValues[i] := rLine[2];
  1003.                     SetDReal(MyLog, FirstStandardID + i - 1, StandardValues[i], 3);
  1004.                     GetLineFromText(rLine, nValues);
  1005.                 until nValues = 0;
  1006.                 nStandards := i;
  1007.                 mCount := nStandards;
  1008.                 for i := 1 to mCount do begin
  1009.                         ClearResults(i);
  1010.                         mean^[i] := umean[i];
  1011.                     end;
  1012.             end
  1013.         else
  1014.             for i := 1 to nStandards do begin
  1015.                     if nValues = nColumns then begin
  1016.                             StandardValues[i] := rLine[nColumns];
  1017.                             SetDReal(MyLog, FirststandardID + i - 1, StandardValues[i], 3);
  1018.                         end;
  1019.                     GetLineFromText(rLine, nValues);
  1020.                 end;
  1021.         InitCursor;
  1022.     end;
  1023.  
  1024.  
  1025.     procedure SaveStandardsToFile (nStandards: integer);
  1026.         var
  1027.             where: Point;
  1028.             reply: SFReply;
  1029.             i: integer;
  1030.             OptionKeyWasDown: boolean;
  1031.     begin
  1032.         OptionKeyWasDown := OptionKeyDown;
  1033.         where.v := 60;
  1034.         where.h := 100;
  1035.         SFPutFile(Where, 'Save Calibration as?', 'Standards', nil, reply);
  1036.         if reply.good then begin
  1037.                 TextBufSize := 0;
  1038.                 for i := 1 to nStandards do begin
  1039.                         PutReal(umean[i], 1, 3);
  1040.                         PutChar(tab);
  1041.                         if StandardValues[i] >= 100.0 then
  1042.                             PutReal(StandardValues[i], 1, 3)
  1043.                         else
  1044.                             PutReal(StandardValues[i], 1, 5);
  1045.                         if i <> nStandards then
  1046.                             PutChar(cr);
  1047.                     end;
  1048.                 with reply do
  1049.                     SaveAsText(fname, vRefNum);
  1050.             end;
  1051.         InitCursor;
  1052.     end;
  1053.  
  1054.  
  1055.     procedure CopyFunctionToLUT;
  1056.         var
  1057.             i: integer;
  1058.             value: LongInt;
  1059.             scale: extended;
  1060.     begin
  1061.         with info^ do begin
  1062.                 DisableDensitySlice;
  1063.                 scale := 65535.0 / (MaxValue - MinValue);
  1064.                 for i := 0 to 255 do begin
  1065.                         value := 65535 - round(scale * (cvalue[i] - MinValue));
  1066.                         with cTable[i].rgb do begin
  1067.                                 red := value;
  1068.                                 green := value;
  1069.                                 blue := value;
  1070.                             end;
  1071.                     end;
  1072.                 LoadLUT(cTable);
  1073.                 LutMode := CustomGrayScale;
  1074.                 SetupPseudocolor;
  1075.                 UpdateMap
  1076.             end;
  1077.     end;
  1078.  
  1079.  
  1080.     procedure SetupUncalibratedOD;
  1081.         var
  1082.             i: integer;
  1083.     begin
  1084.         with info^ do begin
  1085.                 DensityCalibrated := true;
  1086.                 ZeroClip := false;
  1087.                 nCoefficients := 0;
  1088.                 for i := 1 to 6 do
  1089.                     Coefficient[i] := 1.0;
  1090.                 fit := UncalibratedOD;
  1091.                 GenerateValues;
  1092.                 UnitOfMeasure := 'U. OD';
  1093.                 nStandards := 0;
  1094.             end;
  1095.     end;
  1096.  
  1097.  
  1098.     function InvertOD (var temp: StandardsArray): boolean;
  1099.         var
  1100.             i: integer;
  1101.     begin
  1102.         for i := 1 to nStandards do
  1103.             if (StandardValues[i] < 0.000009) or (StandardValues[i] > 4.64) then begin
  1104.                     PutMessage('Known OD Values must be in the range 0.00001 to 4.62.');
  1105.                     InvertOD := false;
  1106.                     exit(InvertOD);
  1107.                 end;
  1108.         for i := 1 to nStandards do  {temp[i] := -log10(1.000 - exp10(-StandardValues[i]));}
  1109.             temp[i] := -0.434294481 * ln(1.000 - exp(-2.302585093 * StandardValues[i]));
  1110.         InvertOD := true;
  1111.     end;
  1112.  
  1113.  
  1114.     procedure Calibrate;
  1115.         const
  1116.             FirstLevelID = 3;
  1117.             FirstStandardID = 23;
  1118.             FirstFitID = 63;
  1119.             LastFitID = 74;
  1120.             UnitOfMeasureID = 75;
  1121.             OpenID = 77;
  1122.             SaveID = 78;
  1123.             CopyID = 81;
  1124.             RemoveID = 82;
  1125.             InvertID = 83;
  1126.         var
  1127.             mylog: DialogPtr;
  1128.             ignore, item, i, nBadReals: integer;
  1129.             str: str255;
  1130.             SaveStandards, temp, NewValues: StandardsArray;
  1131.             OptionKeyWasDown, CopyFunction, RemoveCalibration: boolean;
  1132.     begin
  1133.         OptionKeyWasDown := OptionKeyDown;
  1134.         SaveStandards := StandardValues;
  1135.         CopyFunction := false;
  1136.         RemoveCalibration := false;
  1137.         with info^ do begin
  1138.                 mylog := GetNewDialog(20, nil, pointer(-1));
  1139.                 nStandards := mCount;
  1140.                 if nStandards > MaxStandards then
  1141.                     nStandards := MaxStandards;
  1142.                 for i := 1 to nStandards do begin
  1143.                         SetDReal(MyLog, FirstLevelID + i - 1, umean[i], 2);
  1144.                         if StandardValues[i] <> BadReal then
  1145.                             SetDReal(MyLog, FirstStandardID + i - 1, StandardValues[i], 3);
  1146.                     end;
  1147.                 SelIText(MyLog, FirstStandardID, 0, 32767);
  1148.                 if (fit = SpareFit1) or (fit = SpareFit2) then
  1149.                     fit := Poly3;
  1150.                 SetDialogItem(mylog, FirstFitID + ord(fit), 1);
  1151.                 if DensityCalibrated then
  1152.                     SetDString(MyLog, UnitOfMeasureID, UnitOfMeasure);
  1153.                 repeat
  1154.                     ModalDialog(nil, item);
  1155.                     if (item >= FirstStandardID) and (item < (FirstStandardID + MaxStandards)) then begin
  1156.                             i := item - FirstStandardID + 1;
  1157.                             if i <= nStandards then
  1158.                                 StandardValues[i] := GetDReal(MyLog, item)
  1159.                             else begin
  1160.                                     PutMessage('Before entering known values you must use the Measure command to read a set of standards.');
  1161.                                     SetDString(MyLog, item, '');
  1162.                                 end;
  1163.                         end;
  1164.                     if (item >= FirstLevelID) and (item < (FirstLevelID + MaxStandards)) then begin
  1165.                             i := item - FirstLevelID + 1;
  1166.                             if OptionKeyWasDown and (i <= nStandards) then
  1167.                                 umean[item - FirstLevelID + 1] := GetDReal(MyLog, item)
  1168.                             else begin
  1169.                                     PutMessage('Use the Measure command to record measured values.');
  1170.                                     if i <= nStandards then begin
  1171.                                             RealToString(umean[i], 1, 2, str);
  1172.                                             SetDString(MyLog, item, str)
  1173.                                         end
  1174.                                     else
  1175.                                         SetDString(MyLog, item, '');
  1176.                                 end;
  1177.                         end;
  1178.                     if (item >= FirstFitID) and (item <= LastFitID) then begin
  1179.                             for i := FirstFitID to LastFitID do
  1180.                                 SetDialogItem(mylog, i, 0);
  1181.                             SetDialogItem(mylog, item, 1);
  1182.                             fit := CurveFitType(item - FirstFitID);
  1183.                         end;
  1184.                     if item = UnitOfMeasureID then
  1185.                         UnitOfMeasure := GetDString(MyLog, item);
  1186.                     if item = OpenID then
  1187.                         GetStandardsFromFile(mylog, FirstLevelID, FirstStandardID);
  1188.                     if (item = SaveID) and (nStandards > 1) then
  1189.                         SaveStandardsToFile(nStandards);
  1190.                     if item = CopyID then begin
  1191.                             CopyFunction := not CopyFunction;
  1192.                             if CopyFunction then
  1193.                                 RemoveCalibration := false;
  1194.                             SetDialogItem(mylog, CopyID, ord(CopyFunction));
  1195.                             SetDialogItem(mylog, RemoveID, ord(RemoveCalibration));
  1196.                         end;
  1197.                     if item = RemoveID then begin
  1198.                             RemoveCalibration := not RemoveCalibration;
  1199.                             if RemoveCalibration then
  1200.                                 CopyFunction := false;
  1201.                             SetDialogItem(mylog, RemoveID, ord(RemoveCalibration));
  1202.                             SetDialogItem(mylog, CopyID, ord(CopyFunction));
  1203.                         end;
  1204.                     if (item = InvertID) and (nStandards > 1) then
  1205.                         if InvertOD(NewValues) then
  1206.                             for i := 1 to nStandards do begin
  1207.                                     StandardValues[i] := NewValues[i];
  1208.                                     SetDReal(MyLog, FirstStandardID + i - 1, StandardValues[i], 5);
  1209.                                 end;
  1210.                 until (item = ok) or (item = cancel);
  1211.                 DisposDialog(mylog);
  1212.                 if item = cancel then begin
  1213.                         StandardValues := SaveStandards;
  1214.                         exit(calibrate)
  1215.                     end;
  1216.                 if RemoveCalibration then begin
  1217.                         DensityCalibrated := false;
  1218.                         for i := 0 to 255 do
  1219.                             cvalue[i] := i;
  1220.                         UpdateTitleBar;
  1221.                         exit(calibrate)
  1222.                     end;
  1223.                 nBadReals := 0;
  1224.                 if fit = UncalibratedOD then
  1225.                     SetupUncalibratedOD
  1226.                 else begin
  1227.                         for i := 1 to nStandards do
  1228.                             if StandardValues[i] = BadReal then
  1229.                                 nBadReals := nBadReals + 1;
  1230.                         if (nStandards > 0) and (nBadReals = 0) then
  1231.                             DoCurveFitting
  1232.                         else if not DensityCalibrated then
  1233.                             beep;
  1234.                     end;
  1235.                 if DensityCalibrated then begin
  1236.                         SetupCalibrationPlot;
  1237.                         if CopyFunction then
  1238.                             CopyFunctionToLUT;
  1239.                     end;
  1240.                 UpdateTitleBar;
  1241.             end; {with info^}
  1242.     end;
  1243.  
  1244.  
  1245.     procedure ResetCounter;
  1246.         var
  1247.             AlertID: Integer;
  1248.     begin
  1249.         if UnsavedResults and (not macro) then begin
  1250.                 InitCursor;
  1251.                 AlertID := alert(500, nil);
  1252.             end
  1253.         else
  1254.             AlertID := ok;
  1255.         if AlertID <> CancelResetID then begin
  1256.                 nPoints := 0;
  1257.                 nLengths := 0;
  1258.                 nAngles := 0;
  1259.                 mCount := 0;
  1260.                 mCount2 := 0;
  1261.                 UnsavedResults := false;
  1262.                 ShowValues;
  1263.                 if ResultsWindow <> nil then begin
  1264.                         with ListTE^^ do
  1265.                             TESetSelect(0, teLength, ListTE);
  1266.                         TEDelete(ListTE);
  1267.                         UpdateResultsScrollBars;
  1268.                     end;
  1269.             end;
  1270.         measuring := false;
  1271.     end;
  1272.  
  1273.  
  1274.     procedure ShowResults;
  1275.         const
  1276.             FontSize = 9;
  1277.         var
  1278.             wrect, crect, trect: rect;
  1279.             loc: point;
  1280.     begin
  1281.         mCount2 := mCount;
  1282.         if ResultsWindow <> nil then begin
  1283.                 SelectWindow(ResultsWindow);
  1284.                 exit(ShowResults);
  1285.             end;
  1286.         CopyResultsToBuffer(1, mCount, true);
  1287.         ShowMessage('');
  1288.         ResultsWidth := 110 + round(nListColumns * FieldWidth * 6.5);
  1289.         if ResultsWidth < 250 then
  1290.             ResultsWidth := 250;
  1291.         if (ResultsWidth + 20) > ScreenWidth then
  1292.             ResultsWidth := ScreenWidth - 20;
  1293.         ResultsHeight := ((LongInt(TextBufLineCount) * 2) + 2) * FontSize;
  1294.         if ResultsHeight < 200 then
  1295.             ResultsHeight := 200;
  1296.         if (ResultsHeight + ResultsTop + 50) > ScreenHeight then
  1297.             ResultsHeight := ScreenHeight - ResultsTop - 50;
  1298.         SetRect(wrect, ResultsLeft, ResultsTop, ResultsLeft + ResultsWidth, ResultsTop + ResultsHeight);
  1299.         ResultsWindow := NewWindow(nil, wrect, 'Results', true, 0, pointer(-1), true, 0);
  1300.         WindowPeek(ResultsWindow)^.WindowKind := ResultsKind;
  1301.         SetRect(crect, ResultsWidth - ScrollBarWidth, -1, ResultsWidth + 1, ResultsHeight - 14);
  1302.         vScrollBar := NewControl(ResultsWindow, crect, '', true, 0, 0, ResultsHeight - 14, ScrollBarProc, 0);
  1303.         SetRect(crect, -1, ResultsHeight - ScrollBarWidth, ResultsWidth - 14, ResultsHeight + 1);
  1304.         hScrollBar := NewControl(ResultsWindow, crect, '', true, 0, 0, ResultsWidth - 14, ScrollBarProc, 0);
  1305.         InitResultsTextEdit(Monaco, FontSize);
  1306.         DrawControls(ResultsWindow);
  1307.         WhatToUndo := NothingToUndo;
  1308.     end;
  1309.  
  1310.  
  1311.     procedure DoMeasurementOptions;
  1312.         const
  1313.             FirstID = 3;
  1314.             LastID = 15;
  1315.             RedirectID = 22;
  1316.             IncludeHolesID = 23;
  1317.             AutoID = 24;
  1318.             AdjustID = 25;
  1319.             HeadingsID = 26;
  1320.             MaxMeasurementsID = 21;
  1321.             WidthID = 19;
  1322.             PrecisionID = 17;
  1323.         var
  1324.             mylog: DialogPtr;
  1325.             item, i, SavePrecision, SaveMaxMeasurements, SaveWidth: integer;
  1326.             mtype: MeasurementTypes;
  1327.             SaveMeasurements: set of MeasurementTypes;
  1328.             SaveRedirect: boolean;
  1329.             SaveAuto, SaveAdjust, SaveHeadings: boolean;
  1330.     begin
  1331.         InitCursor;
  1332.         if nPoints > 0 then
  1333.             Measurements := Measurements + [XYLocM];
  1334.         if nLengths > 0 then
  1335.             Measurements := Measurements + [LengthM];
  1336.         if nAngles > 0 then
  1337.             Measurements := Measurements + [AngleM];
  1338.         SaveMeasurements := measurements;
  1339.         SaveRedirect := RedirectSampling;
  1340.         SaveWidth := FieldWidth;
  1341.         SavePrecision := precision;
  1342.         SaveAuto := WandAutoMeasure;
  1343.         SaveAdjust := WandAdjustAreas;
  1344.         SaveMaxMeasurements := MaxMeasurements;
  1345.         SaveHeadings := ShowHeadings;
  1346.         mylog := GetNewDialog(4000, nil, pointer(-1));
  1347.         mtype := AreaM;
  1348.         for i := FirstID to LastID do begin
  1349.                 if mtype in measurements then
  1350.                     SetDialogItem(mylog, i, 1);
  1351.                 if i <> LastID then
  1352.                     mtype := succ(mtype);
  1353.             end;
  1354.         SetDialogItem(mylog, RedirectID, ord(RedirectSampling));
  1355.         SetDialogItem(mylog, IncludeHolesID, ord(IncludeHoles));
  1356.         SetDialogItem(mylog, AutoID, ord(WandAutoMeasure));
  1357.         SetDialogItem(mylog, AdjustID, ord(WandAdjustAreas));
  1358.         SetDialogItem(mylog, HeadingsID, ord(ShowHeadings));
  1359.         SetDNum(MyLog, MaxMeasurementsID, MaxMeasurements);
  1360.         SetDNum(MyLog, WidthID, FieldWidth);
  1361.         SetDNum(MyLog, PrecisionID, precision);
  1362.         repeat
  1363.             ModalDialog(nil, item);
  1364.             if (item >= FirstID) and (item <= LastID) then begin
  1365.                     i := item - FirstID;
  1366.                     case i of
  1367.                         0: 
  1368.                             mtype := AreaM;
  1369.                         1: 
  1370.                             mtype := MeanM;
  1371.                         2: 
  1372.                             mtype := StdDevM;
  1373.                         3: 
  1374.                             mtype := xyLocM;
  1375.                         4: 
  1376.                             mtype := ModeM;
  1377.                         5: 
  1378.                             mtype := LengthM;
  1379.                         6: 
  1380.                             mtype := MajorAxisM;
  1381.                         7: 
  1382.                             mtype := MinorAxisM;
  1383.                         8: 
  1384.                             mtype := AngleM;
  1385.                         9: 
  1386.                             mtype := IntDenM;
  1387.                         10: 
  1388.                             mtype := MinMaxM;
  1389.                         11: 
  1390.                             mtype := User1M;
  1391.                         12: 
  1392.                             mtype := User2M;
  1393.                     end;
  1394.                     if mtype in measurements then begin
  1395.                             measurements := measurements - [mtype];
  1396.                             SetDialogItem(mylog, item, 0)
  1397.                         end
  1398.                     else begin
  1399.                             measurements := measurements + [mtype];
  1400.                             SetDialogItem(mylog, item, 1)
  1401.                         end;
  1402.                 end;
  1403.             if item = RedirectID then begin
  1404.                     RedirectSampling := not RedirectSampling;
  1405.                     SetDialogItem(mylog, RedirectID, ord(RedirectSampling));
  1406.                 end;
  1407.             if item = IncludeHolesID then begin
  1408.                     IncludeHoles := not IncludeHoles;
  1409.                     SetDialogItem(mylog, IncludeHolesID, ord(IncludeHoles));
  1410.                 end;
  1411.             if item = AutoID then begin
  1412.                     WandAutoMeasure := not WandAutoMeasure;
  1413.                     SetDialogItem(mylog, AutoID, ord(WandAutoMeasure));
  1414.                 end;
  1415.             if item = AdjustID then begin
  1416.                     WandAdjustAreas := not WandAdjustAreas;
  1417.                     SetDialogItem(mylog, AdjustID, ord(WandAdjustAreas));
  1418.                 end;
  1419.             if item = HeadingsID then begin
  1420.                     ShowHeadings := not ShowHeadings;
  1421.                     SetDialogItem(mylog, HeadingsID, ord(ShowHeadings));
  1422.                 end;
  1423.             if item = WidthID then
  1424.                 FieldWidth := GetDNum(MyLog, WidthID);
  1425.             if item = PrecisionID then
  1426.                 precision := GetDNum(MyLog, PrecisionID);
  1427.             if item = MaxMeasurementsID then
  1428.                 MaxMeasurements := GetDNum(MyLog, MaxMeasurementsID);
  1429.         until (item = ok) or (item = cancel);
  1430.         DisposDialog(mylog);
  1431.         if (FieldWidth < 1) or (FieldWidth > 18) then begin
  1432.                 FieldWidth := SaveWidth;
  1433.                 beep;
  1434.             end;
  1435.         if (precision < 0) or (precision > 8) then begin
  1436.                 precision := SavePrecision;
  1437.                 beep;
  1438.             end;
  1439.         if (MaxMeasurements < 1) or (MaxMeasurements > MaxMaxRegions) then begin
  1440.                 MaxMeasurements := SaveMaxMeasurements;
  1441.                 beep;
  1442.             end;
  1443.         if item = cancel then begin
  1444.                 measurements := SaveMeasurements;
  1445.                 RedirectSampling := SaveRedirect;
  1446.                 FieldWidth := SaveWidth;
  1447.                 precision := SavePrecision;
  1448.                 WandAutoMeasure := SaveAuto;
  1449.                 WandAdjustAreas := SaveAdjust;
  1450.                 MaxMeasurements := SaveMaxMeasurements;
  1451.                 ShowHeadings := SaveHeadings;
  1452.             end;
  1453.         if not (XYLocM in Measurements) then
  1454.             nPoints := 0;
  1455.         if not (LengthM in Measurements) then
  1456.             nLengths := 0;
  1457.         if not (AngleM in Measurements) then
  1458.             nAngles := 0;
  1459.         UpdateFitEllipse;
  1460.         if MaxMeasurements <> SaveMaxMeasurements then
  1461.             PutMessage('You must "Record Preferences" and restart before the change to Maximum Particles will take effect.');
  1462.         if (Measurements <> SaveMeasurements) or (SaveWidth <> FieldWidth) or (SavePrecision <> Precision) then
  1463.             UpdateList;
  1464.     end;
  1465.  
  1466.  
  1467.     procedure UpdateRoiLineWidth;
  1468.     begin
  1469.         with info^, info^.RoiRect do
  1470.             if RoiShowing and (RoiType = LineRoi) then begin
  1471.                     LX1 := left + LX1;
  1472.                     LY1 := top + LY1;
  1473.                     LX2 := left + LX2;
  1474.                     LY2 := top + LY2;
  1475.                     MakeRegion;
  1476.                 end;
  1477.     end;
  1478.  
  1479.  
  1480.     procedure DoProfilePlotOptions;
  1481.         const
  1482.             FixedScaleID = 7;
  1483.             MinID = 8;
  1484.             MaxID = 9;
  1485.             FixedSizeID = 10;
  1486.             WidthID = 11;
  1487.             HeightID = 12;
  1488.             LineWidthID = 13;
  1489.             LinePlotID = 14;
  1490.             ScatterPlotID = 15;
  1491.             InvertID = 16;
  1492.             LabelsID = 17;
  1493.         var
  1494.             mylog: DialogPtr;
  1495.             item, i: integer;
  1496.             SaveAutoscale, SaveLinePlot, SaveInvert, SaveDrawLabels, SaveFixedSize: boolean;
  1497.             SaveWidth, SaveHeight, SaveLineWidth, SaveLineIndex: integer;
  1498.             SaveMin, SaveMax: extended;
  1499.     begin
  1500.         InitCursor;
  1501.         SaveAutoscale := AutoscalePlots;
  1502.         SaveLinePlot := LinePlot;
  1503.         SaveInvert := InvertPlots;
  1504.         SaveMin := ProfilePlotMin;
  1505.         SaveMax := ProfilePlotMax;
  1506.         SaveLineWidth := LineWidth;
  1507.         SaveLineIndex := LineIndex;
  1508.         mylog := GetNewDialog(5000, nil, pointer(-1));
  1509.         SetDialogItem(mylog, FixedScaleID, ord(not AutoscalePlots));
  1510.         SetDReal(MyLog, MinID, ProfilePlotMin, 2);
  1511.         SetDReal(MyLog, MaxID, ProfilePlotMax, 2);
  1512.         SetDialogItem(mylog, FixedSizeID, ord(FixedSizePlot));
  1513.         SetDNum(MyLog, WidthID, ProfilePlotWidth);
  1514.         SetDNum(MyLog, HeightID, ProfilePlotHeight);
  1515.         if LinePlot then
  1516.             SetDialogItem(mylog, LinePlotID, 1)
  1517.         else
  1518.             SetDialogItem(mylog, ScatterPlotID, 1);
  1519.         if InvertPlots then
  1520.             SetDialogItem(mylog, InvertID, 1);
  1521.         if DrawPlotLabels then
  1522.             SetDialogItem(mylog, LabelsID, 1);
  1523.         SetDNum(MyLog, LineWidthID, LineWidth);
  1524.         repeat
  1525.             ModalDialog(nil, item);
  1526.             if item = FixedScaleID then begin
  1527.                     AutoscalePlots := not AutoscalePlots;
  1528.                     SetDialogItem(mylog, FixedScaleID, ord(not AutoscalePlots));
  1529.                 end;
  1530.             if item = MinID then begin
  1531.                     ProfilePlotMin := GetDReal(MyLog, MinID);
  1532.                     AutoscalePlots := false;
  1533.                     SetDialogItem(mylog, FixedScaleID, 1);
  1534.                 end;
  1535.             if item = MaxID then begin
  1536.                     ProfilePlotMax := GetDReal(MyLog, MaxID);
  1537.                     AutoscalePlots := false;
  1538.                     SetDialogItem(mylog, FixedScaleID, 1);
  1539.                 end;
  1540.             if item = FixedSizeID then begin
  1541.                     FixedSizePlot := not FixedSizePlot;
  1542.                     SetDialogItem(mylog, FixedSizeID, ord(FixedSizePlot));
  1543.                 end;
  1544.             if item = WidthID then begin
  1545.                     ProfilePlotWidth := GetDNum(MyLog, WidthID);
  1546.                     if (ProfilePlotWidth < 0) or (ProfilePlotWidth > 1023) then begin
  1547.                             ProfilePlotWidth := SaveWidth;
  1548.                             SetDNum(MyLog, WidthID, ProfilePlotWidth);
  1549.                         end;
  1550.                     FixedSizePlot := true;
  1551.                     SetDialogItem(mylog, FixedSizeID, 1);
  1552.                 end;
  1553.             if item = HeightID then begin
  1554.                     ProfilePlotHeight := GetDNum(MyLog, HeightID);
  1555.                     if (ProfilePlotHeight < 0) or (ProfilePlotHeight > 1023) then begin
  1556.                             ProfilePlotHeight := SaveHeight;
  1557.                             SetDNum(MyLog, HeightID, ProfilePlotHeight);
  1558.                         end;
  1559.                     FixedSizePlot := true;
  1560.                     SetDialogItem(mylog, FixedSizeID, 1);
  1561.                 end;
  1562.             if (item = LinePlotID) or (item = ScatterPlotID) then begin
  1563.                     SetDialogItem(mylog, LinePlotID, 0);
  1564.                     SetDialogItem(mylog, ScatterPlotID, 0);
  1565.                     SetDialogItem(mylog, item, 1);
  1566.                     LinePlot := item = LinePlotID;
  1567.                 end;
  1568.             if item = InvertID then begin
  1569.                     InvertPlots := not InvertPlots;
  1570.                     SetDialogItem(mylog, InvertID, ord(InvertPlots));
  1571.                 end;
  1572.             if item = LabelsID then begin
  1573.                     DrawPlotLabels := not DrawPlotLabels;
  1574.                     if DrawPlotLabels then {Attempt to fix a "sticky" check box bug.}
  1575.                         SetDialogItem(mylog, LabelsID, 1)
  1576.                     else
  1577.                         SetDialogItem(mylog, LabelsID, 0);
  1578.                 end;
  1579.             if item = LineWidthID then begin
  1580.                     LineWidth := GetDNum(MyLog, LineWidthID);
  1581.                     if (LineWidth < 1) or (LineWidth > 500) then begin
  1582.                             LineWidth := SaveLineWidth;
  1583.                             SetDNum(MyLog, LineWidthID, LineWidth);
  1584.                         end;
  1585.                     ShowLineWidth;
  1586.                 end;
  1587.         until (item = ok) or (item = cancel);
  1588.         DisposDialog(mylog);
  1589.         if item = cancel then begin
  1590.                 AutoscalePlots := SaveAutoscale;
  1591.                 LinePlot := SaveLinePlot;
  1592.                 InvertPlots := SaveInvert;
  1593.                 ProfilePlotMin := SaveMin;
  1594.                 ProfilePlotMax := SaveMax;
  1595.                 DrawPlotLabels := SaveDrawLabels;
  1596.                 LineWidth := SaveLineWidth;
  1597.                 if LineIndex <> SaveLineIndex then begin
  1598.                         LineIndex := SaveLineIndex;
  1599.                         DrawTools;
  1600.                     end;
  1601.             end;
  1602.         if LineWidth <> SaveLineWidth then
  1603.             UpdateRoiLineWidth;
  1604.         if ProfilePlotMax <= ProfilePlotMin then begin
  1605.                 beep;
  1606.                 ProfilePlotMin := SaveMin;
  1607.                 ProfilePlotMax := SaveMax;
  1608.             end;
  1609.     end;
  1610.  
  1611.  
  1612.     procedure DoPoints (event: EventRecord);
  1613.         var
  1614.             loc, tloc: point;
  1615.             hloc, vloc, y, offset: integer;
  1616.             r: rect;
  1617.             str, str1, str2: str255;
  1618.             Decrement: boolean;
  1619.     begin
  1620.         Decrement := false;
  1621.         SetPort(GrafPtr(info^.osPort));
  1622.         pmForeColor(ForegroundIndex);
  1623.         loc := event.where;
  1624.         ScreenToOffscreen(loc);
  1625.         with loc do begin
  1626.                 hloc := h;
  1627.                 vloc := v;
  1628.             end;
  1629.         with results, Info^ do begin
  1630.                 nPoints := nPoints + 1;
  1631.                 IncrementCounter;
  1632.                 if InvertYCoordinates then
  1633.                     y := info^.PicRect.bottom - vloc - 1
  1634.                 else
  1635.                     y := vloc;
  1636.                 ClearResults(mCount);
  1637.                 PixelCount^[mCount] := 1;
  1638.                 if SpatiallyCalibrated then
  1639.                     mArea^[mCount] := 1 / xSpatialScale * ySpatialScale
  1640.                 else
  1641.                     mArea^[mCount] := 1;
  1642.                 mean^[mCount] := cvalue[MyGetPixel(hloc, vloc)];
  1643.                 with info^ do
  1644.                     if SpatiallyCalibrated then begin
  1645.                             xcenter^[mCount] := hloc / xSpatialScale;
  1646.                             ycenter^[mCount] := y / ySpatialScale;
  1647.                         end
  1648.                     else begin
  1649.                             xcenter^[mCount] := hloc;
  1650.                             ycenter^[mCount] := y;
  1651.                         end;
  1652.             end;
  1653.         PenNormal;
  1654.         if OptionKeyDown then begin
  1655.                 NumToString(mCount, str);
  1656.                 tloc := loc;
  1657.                 tloc.v := tloc.v + CurrentSize div 3;
  1658.                 DrawTextString(str, tloc, TeJustCenter);
  1659.             end
  1660.         else begin
  1661.                 offset := LineWidth div 2;
  1662.                 SetRect(r, hloc - offset, vloc - offset, hloc + offset + 1, vloc + offset + 1);
  1663.                 if ShiftKeyDown then begin
  1664.                         Decrement := true;
  1665.                         EraseOval(r);
  1666.                         mcount := mcount - 2;
  1667.                         if mcount <= 0 then begin
  1668.                                 mcount := 0;
  1669.                                 UnsavedResults := false;
  1670.                             end;
  1671.                         nPoints := nPoints - 2;
  1672.                         if nPoints < 0 then
  1673.                             nPoints := 0;
  1674.                     end
  1675.                 else
  1676.                     PaintOval(r);
  1677.                 UpdateScreen(r);
  1678.                 if ControlKeyDown then
  1679.                     with info^ do begin
  1680.                             if SpatiallyCalibrated then begin
  1681.                                     RealToString(hloc / xSpatialScale, 1, Precision, str1);
  1682.                                     RealToString(y / ySpatialScale, 1, Precision, str2);
  1683.                                 end
  1684.                             else begin
  1685.                                     NumToString(hloc, str1);
  1686.                                     NumToString(y, str2);
  1687.                                 end;
  1688.                             tloc := loc;
  1689.                             with tloc do begin
  1690.                                     h := h + offset + 5;
  1691.                                     v := v + CurrentSize div 3;
  1692.                                 end;
  1693.                             str := concat('(', str1, ', ', str2, ')');
  1694.                             DrawTextString(str, tloc, TeJustLeft);
  1695.                         end; {Control Key Down}
  1696.             end;
  1697.         ValuesMessage := '';
  1698.         ShowValues;
  1699.         if Decrement then begin
  1700.                 DeleteLines(mcount + 1, mcount + 1);
  1701.                 WhatToUndo := NothingToUndo;
  1702.             end
  1703.         else begin
  1704.                 AppendResults;
  1705.                 if (nPoints = 1) then
  1706.                     if not (XYlocM in Measurements) then
  1707.                         UpdateList;
  1708.                 measuring := true;
  1709.                 WhatToUndo := UndoPoint;
  1710.             end;
  1711.     end;
  1712.  
  1713.  
  1714.     procedure FindAngle (event: EventRecord);
  1715.         var
  1716.             start, finish, OldFinish, MidPoint, first: point;
  1717.             ticks: LongInt;
  1718.             x1, y1, x2, y2: integer;
  1719.             angle, angle1, angle2: real;
  1720.             StartRect: rect;
  1721.             FirstLineDone: boolean;
  1722.     begin
  1723.         DrawLabels('Angle:', '', '');
  1724.         FlushEvents(EveryEvent, 0);
  1725.         start := event.where;
  1726.         Pt2Rect(start, start, StartRect);
  1727.         InsetRect(StartRect, -2, -2);
  1728.         finish := start;
  1729.         SetPort(info^.wptr);
  1730.         PenNormal;
  1731.         PenMode(PatXor);
  1732.         PenSize(1, 1);
  1733.         MoveTo(start.h, start.v);
  1734.         first := start;
  1735.         repeat
  1736.             repeat
  1737.                 OldFinish := finish;
  1738.                 GetMouse(finish);
  1739.                 MoveTo(start.h, start.v);
  1740.                 LineTo(OldFinish.h, OldFinish.v);
  1741.                 MoveTo(start.h, start.v);
  1742.                 LineTo(finish.h, finish.v);
  1743.                 ticks := TickCount;
  1744.                 while ticks = TickCount do
  1745.                     ;
  1746.                 x1 := finish.h - start.h;
  1747.                 y1 := start.v - finish.v;
  1748.                 GetAngle(x1, y1, angle1);
  1749.                 Show1Value(angle1, NoValue);
  1750.             until GetNextEvent(mUpMask, event);
  1751.             FirstLineDone := not PtInRect(finish, StartRect);
  1752.             if not FirstLineDone then
  1753.                 start := finish;
  1754.         until FirstLineDone;
  1755.         MidPoint := finish;
  1756.         x1 := start.h - MidPoint.h;
  1757.         y1 := MidPoint.v - start.v;
  1758.         GetAngle(x1, y1, angle1);
  1759.         start := finish;
  1760.         finish := start;
  1761.         repeat
  1762.             OldFinish := finish;
  1763.             GetMouse(finish);
  1764.             MoveTo(start.h, start.v);
  1765.             LineTo(OldFinish.h, OldFinish.v);
  1766.             MoveTo(start.h, start.v);
  1767.             LineTo(finish.h, finish.v);
  1768.             ticks := TickCount;
  1769.             while ticks = TickCount do
  1770.                 ;
  1771.             x2 := finish.h - MidPoint.h;
  1772.             y2 := MidPoint.v - finish.v;
  1773.             GetAngle(x2, y2, angle2);
  1774.             with results do begin
  1775.                     if angle1 >= angle2 then
  1776.                         angle := angle1 - angle2
  1777.                     else
  1778.                         angle := angle2 - angle1;
  1779.                     if angle > 180.0 then
  1780.                         angle := 360.0 - angle;
  1781.                     Show1Value(angle, NoValue);
  1782.                 end;
  1783.         until GetNextEvent(mUpMask, event);
  1784.         nAngles := nAngles + 1;
  1785.         IncrementCounter;
  1786.         ClearResults(mCount);
  1787.         Orientation^[mCount] := angle;
  1788.         ValuesMessage := '';
  1789.         ShowValues;
  1790.         AppendResults;
  1791.         if nAngles = 1 then
  1792.             UpdateList;
  1793.         repeat
  1794.         until not GetNextEvent(EveryEvent, Event); {FlushEvent doesn't work under A/UX!}
  1795.         xCoordinates^[1] := first.h;
  1796.         yCoordinates^[1] := first.v;
  1797.         xCoordinates^[2] := midpoint.h;
  1798.         yCoordinates^[2] := midpoint.v;
  1799.         xCoordinates^[3] := finish.h;
  1800.         yCoordinates^[3] := finish.v;
  1801.         nCoordinates := 3;
  1802.         MakeNonStraightLineRoi(SegLineRoi);
  1803.     end;
  1804.  
  1805.  
  1806.     procedure SaveBlankField;
  1807.         var
  1808.             SaveInfo: InfoPtr;
  1809.             i, xLines, xPixelsPerLine: integer;
  1810.             src, dst: ptr;
  1811.             SaveFlag: boolean;
  1812.             name: str255;
  1813.     begin
  1814.         if (info^.PictureType = FrameGrabberType) or (info^.PictureType = ScionType) then begin
  1815.                 GetWTitle(info^.wptr, name);
  1816.                 if pos('(Corrected)', name) > 0 then begin
  1817.                         PutMessage('To save a blank field the captured image must be uncorrected.');
  1818.                         exit(SaveBlankField);
  1819.                     end;
  1820.                 SaveInfo := info;
  1821.                 if BlankFieldInfo = nil then begin
  1822.                         if not Duplicate('Blank Field', true) then
  1823.                             exit(SaveBlankField);
  1824.                     end;
  1825.                 src := info^.PicBaseAddr;
  1826.                 dst := BlankFieldInfo^.PicBaseAddr;
  1827.                 with Info^.PicRect do begin
  1828.                         xLines := bottom - top;
  1829.                         xPixelsPerLine := right - left;
  1830.                     end;
  1831.                 for i := 1 to xLines do begin
  1832.                         BlockMove(src, dst, xPixelsPerLine);
  1833.                         src := ptr(ord4(src) + info^.BytesPerRow);
  1834.                         dst := ptr(ord4(dst) + xPixelsPerLine);
  1835.                     end;
  1836.                 Info := BlankFieldInfo;
  1837.                 InvertPic;
  1838.                 SaveFlag := digitizing;
  1839.                 digitizing := false;
  1840.                 SelectAll(false);
  1841.                 ShowCount := false;
  1842.                 Measure;
  1843.                 ShowCount := true;
  1844.                 digitizing := SaveFlag;
  1845.                 BlankFieldMean := round(results.UncalibratedMean);
  1846.                 UndoLastMeasurement(false);
  1847.                 KillRoi;
  1848.                 UpdatePicWindow;
  1849.                 info := SaveInfo;
  1850.                 SelectWindow(Info^.wptr);
  1851.             end;
  1852.     end;
  1853.  
  1854.  
  1855.     procedure UndoLastMeasurement (DisplayResults: boolean);
  1856.     begin
  1857.         if mCount > 0 then begin
  1858.                 if DisplayResults then
  1859.                     DeleteLines(mCount, mCount);
  1860.                 mCount := mCount - 1;
  1861.                 if mCount = 0 then
  1862.                     UnsavedResults := false;
  1863.             end
  1864.         else
  1865.             WhatToUndo := NothingToUndo;
  1866.         if DisplayResults then
  1867.             ShowValues;
  1868.     end;
  1869.  
  1870.  
  1871.     function PixelInside (hloc, vloc: integer): boolean;
  1872.         var
  1873.             value: integer;
  1874.     begin
  1875.         value := MyGetPixel(hloc, vloc);
  1876.         case ThresholdingMode of
  1877.             DensitySlice: 
  1878.                 PixelInside := (value >= SliceStart) and (value <= SliceEnd);
  1879.             GrayMapThresholding: 
  1880.                 PixelInside := value >= GrayMapThreshold;
  1881.             BinaryImage: 
  1882.                 PixelInside := value = BlackIndex;
  1883.         end;
  1884.     end;
  1885.  
  1886.  
  1887.     function TraceEdge (hstart, vstart: integer; StartingDirection: char; var TouchingEdge: boolean): boolean;
  1888.  
  1889.    {Traces the points(not pixels) that define the edge of an object using the following}
  1890.    {16 entry lookup table and converts the resulting outline to a QuickDraw region.}
  1891.  
  1892.       {Index  1234*  Code    Result}
  1893.  
  1894.       {0          0000     X      Should never happen}
  1895.       {1          000X     R      Go Right}
  1896.       {2          00X0     D     Go Down}
  1897.       {3          00XX     R     Go Right}
  1898.       {4          0X00     U     Go Up}
  1899.       {5          0X0X     U     Go Up}
  1900.       {6          0XX0     u      Go up or down depending on current direction}
  1901.       {7         0XXX     U      Go up}
  1902.       {8          X000     L      Go left}
  1903.       {9          X00X     l       Go left or right depending on current direction}
  1904.       {10       X0X0     D      Go down}
  1905.       {11        X0XX    R      Go right}
  1906.       {12        XX00     L      Go left}
  1907.       {13        XX0X     L      Go left}
  1908.       {14        XXX0     D     Go down}
  1909.       {15        XXXX     X     Should never happen}
  1910.  
  1911.        {*   1=Upper left pixel,  2=Upper right pixel, 3=Lower left pixel, 4=Lower right pixel}
  1912.  
  1913.         var
  1914.             count, hloc, vloc, hold, vold, index, SaveBackground: integer;
  1915.             Saveport: GrafPtr;
  1916.             direction, NewDirection: char;
  1917.             table: string[16];
  1918.             UL, UR, LL, LR, SaveCoordinates: boolean;
  1919.             TempRgn: RgnHandle;
  1920.     begin
  1921.         TouchingEdge := false;
  1922.         table := 'XRDRUUuULlDRLLDX';
  1923.         GetPort(SavePort);
  1924.         SetPort(GrafPtr(info^.osPort));
  1925.         if SelectionMode <> NewSelection then
  1926.             TempRgn := NewRgn;
  1927.         with info^ do begin
  1928.                 SaveBackground := BackgroundIndex; {We want MyGetPixel to always return 0}
  1929.                 BackgroundIndex := WhiteIndex;         {for coordinates beyond the edge of the image.}
  1930.                 PenNormal;
  1931.                 OpenRgn;
  1932.                 direction := StartingDirection;
  1933.                 hloc := hstart;
  1934.                 vloc := vstart;
  1935.                 UL := PixelInside(hloc - 1, vloc - 1);
  1936.                 UR := PixelInside(hloc, vloc - 1);
  1937.                 LL := PixelInside(hloc - 1, vloc);
  1938.                 LR := PixelInside(hloc, vloc);
  1939.                 hold := hstart;
  1940.                 vold := vstart;
  1941.                 MoveTo(hstart, vstart);
  1942.                 count := 0;
  1943.                 SaveCoordinates := ((CurrentTool = wand) or (LengthM in Measurements)) and (not MakingLOI);
  1944.                 if SaveCoordinates then begin
  1945.                         xCoordinates^[1] := hstart;
  1946.                         yCoordinates^[1] := vstart;
  1947.                         count := 1;
  1948.                     end;
  1949.                 repeat
  1950.                     if IgnoreParticlesTouchingEdge then
  1951.                         with info^.PicRect do
  1952.                             TouchingEdge := TouchingEdge or (hloc = left) or (hloc = right) or (vloc = top) or (vloc = bottom);
  1953.                     count := count + 1;
  1954.                     index := 0;
  1955.                     if LR then
  1956.                         index := bor(index, 1);
  1957.                     if LL then
  1958.                         index := bor(index, 2);
  1959.                     if UR then
  1960.                         index := bor(index, 4);
  1961.                     if UL then
  1962.                         index := bor(index, 8);
  1963.                     NewDirection := table[index + 1];
  1964.                     if NewDirection = 'u' then begin
  1965.                             if direction = 'R' then
  1966.                                 NewDirection := 'U'
  1967.                             else
  1968.                                 NewDirection := 'D'
  1969.                         end;
  1970.                     if NewDirection = 'l' then begin
  1971.                             if direction = 'U' then
  1972.                                 NewDirection := 'L'
  1973.                             else
  1974.                                 NewDirection := 'R'
  1975.                         end;
  1976.                     case NewDirection of
  1977.                         'U':  begin
  1978.                                 vloc := vloc - 1;
  1979.                                 LL := UL;
  1980.                                 LR := UR;
  1981.                                 UL := PixelInside(hloc - 1, vloc - 1);
  1982.                                 UR := PixelInside(hloc, vloc - 1);
  1983.                             end;
  1984.                         'D':  begin
  1985.                                 vloc := vloc + 1;
  1986.                                 UL := LL;
  1987.                                 UR := LR;
  1988.                                 LL := PixelInside(hloc - 1, vloc);
  1989.                                 LR := PixelInside(hloc, vloc);
  1990.                             end;
  1991.                         'L':  begin
  1992.                                 hloc := hloc - 1;
  1993.                                 UR := UL;
  1994.                                 LR := LL;
  1995.                                 UL := PixelInside(hloc - 1, vloc - 1);
  1996.                                 LL := PixelInside(hloc - 1, vloc);
  1997.                             end;
  1998.                         'R':  begin
  1999.                                 hloc := hloc + 1;
  2000.                                 UL := UR;
  2001.                                 LL := LR;
  2002.                                 UR := PixelInside(hloc, vloc - 1);
  2003.                                 LR := PixelInside(hloc, vloc);
  2004.                             end;
  2005.                     end;
  2006.                     LineTo(hloc, vloc);
  2007.                     if SaveCoordinates then begin
  2008.                             xCoordinates^[count] := hloc;
  2009.                             yCoordinates^[count] := vloc;
  2010.                         end;
  2011.                     hold := hloc;
  2012.                     vold := vloc;
  2013.                     direction := NewDirection;
  2014.                 until ((hloc = hstart) and (vloc = vstart) and (direction = StartingDirection)) or (count >= MaxCoordinates);
  2015.                 if SelectionMode <> NewSelection then
  2016.                     CloseRgn(TempRgn)
  2017.                 else
  2018.                     CloseRgn(roiRgn);
  2019.                 if count >= MaxCoordinates then begin
  2020.                         SetEmptyRgn(roiRgn);
  2021.                         SetPort(SavePort);
  2022.                         TraceEdge := false;
  2023.                         BackgroundIndex := SaveBackground;
  2024.                         nCoordinates := 0;
  2025.                         exit(TraceEdge);
  2026.                     end;
  2027.                 if (SelectionMode = AddSelection) then begin
  2028.                         if RgnNotTooBig(roiRgn, TempRgn) then
  2029.                             UnionRgn(roiRgn, TempRgn, roiRgn);
  2030.                     end
  2031.                 else if (SelectionMode = SubSelection) then begin
  2032.                         if RgnNotTooBig(roiRgn, TempRgn) then
  2033.                             DiffRgn(roiRgn, TempRgn, roiRgn);
  2034.                     end;
  2035.                 RoiShowing := true;
  2036.                 roiType := FreehandRoi;
  2037.                 if SelectionMode = SubSelection then
  2038.                     UpdateScreen(RoiRect);
  2039.                 RoiRect := roiRgn^^.rgnBBox;
  2040.                 BackgroundIndex := SaveBackground;
  2041.             end; {with info}
  2042.         if SelectionMode <> NewSelection then
  2043.             DisposeRgn(TempRgn);
  2044.         SetPort(SavePort);
  2045.         if SaveCoordinates then begin
  2046.                 nCoordinates := count - 1;
  2047.                 if CurrentTool = wand then
  2048.                     MakeCoordinatesRelative;
  2049.             end;
  2050.         TraceEdge := true;
  2051.     end;
  2052.  
  2053.  
  2054.     procedure MarkSelection (count: integer);
  2055.         var
  2056.             SavePort: GrafPtr;
  2057.             NumWidth, NumLeft, NumBottom, SaveForegroundIndex: integer;
  2058.             RoiWidth, inset, hcenter, vcenter: integer;
  2059.             str: str255;
  2060.             r: rect;
  2061.             OutlineWithEllipse: boolean;
  2062.             xc, yc: extended;
  2063.     begin
  2064.         OutlineWithEllipse := FitEllipse and OptionKeyWasDown;
  2065.         with info^ do begin
  2066.                 KillRoi;
  2067.                 SetupUndo;
  2068.                 WhatToUndo := UndoOutline;
  2069.                 GetPort(SavePort);
  2070.                 SetPort(GrafPtr(osPort));
  2071.                 SaveForegroundIndex := ForegroundIndex;
  2072.                 SetForegroundColor(WhiteIndex);
  2073.                 PenNormal;
  2074.                 TextFont(ApplFont);
  2075.                 TextSize(9);
  2076.                 NumToString(count, str);
  2077.                 with RoiRect do begin
  2078.                         NumWidth := StringWidth(str);
  2079.                         if AnalyzingParticles or OutlineWithEllipse then begin
  2080.                                 xc := xcenter^[count];
  2081.                                 yc := ycenter^[count];
  2082.                                 if SpatiallyCalibrated then begin
  2083.                                         xc := xc * xSpatialScale;
  2084.                                         yc := yc * ySpatialScale;
  2085.                                     end;
  2086.                                 hcenter := round(xc);
  2087.                                 vcenter := round(yc);
  2088.                                 if InvertYCoordinates then
  2089.                                     vcenter := PicRect.bottom - vcenter - 1
  2090.                             end
  2091.                         else begin
  2092.                                 hcenter := left + (right - left) div 2;
  2093.                                 vcenter := top + (bottom - top) div 2;
  2094.                             end;
  2095.                         NumLeft := hcenter - NumWidth div 2;
  2096.                         NumBottom := vcenter + 3;
  2097.                         if not BinaryPic and not AnalyzingParticles then begin
  2098.                                 FrameRgn(roiRgn);
  2099.                                 if OutlineWithEllipse then
  2100.                                     DrawEllipse;
  2101.                             end;
  2102.                     end;
  2103.                 PenNormal;
  2104.                 SetRect(r, NumLeft - 1, NumBottom - 9, NumLeft + NumWidth + 1, NumBottom + 1);
  2105.                 PaintRoundRect(r, 4, 4);
  2106.                 MoveTo(NumLeft, NumBottom);
  2107.                 TextMode(srcXor);
  2108.                 DrawString(str);
  2109.                 SetForegroundColor(SaveForegroundIndex);
  2110.                 if not analyzingParticles then
  2111.                     UpdateScreen(RoiRect);
  2112.                 SetPort(SavePort);
  2113.                 changes := true;
  2114.             end;
  2115.     end;
  2116.  
  2117.  
  2118.     function isBinaryImage: boolean;
  2119.         var
  2120.             SaveRoiRect: rect;
  2121.             SaveRedirectFlag: boolean;
  2122.     begin
  2123.         with info^ do begin
  2124.                 SaveRoiRect := RoiRect;
  2125.                 RoiRect := PicRect;
  2126.                 if RedirectSampling then
  2127.                     GetHistogram
  2128.                 else
  2129.                     GetRectHistogram;
  2130.                 BinaryPic := (histogram[0] + histogram[255]) = LongInt(PixelsPerLine) * nLines;
  2131.                 isBinaryImage := BinaryPic;
  2132.                 RoiRect := SaveRoiRect;
  2133.             end;
  2134.     end;
  2135.  
  2136.  
  2137.     function SetupAutoOutline (BinaryPixel: boolean): boolean;
  2138.     begin
  2139.         SetupAutoOutline := false;
  2140.         FindThresholdingMode;
  2141.         if (ThresholdingMode = NoThresholding) or MakingLOI then
  2142.             if isBinaryImage or BinaryPixel then
  2143.                 ThresholdingMode := BinaryImage;
  2144.         if ThresholdingMode = NoThresholding then begin
  2145.                 PutMessage('Sorry, but you must be thresholding, or working with a binary image, to use the wand tool or to do particle analysis.');
  2146.                 exit(SetupAutoOutline);
  2147.             end;
  2148.         if (ThresholdingMode = GrayMapThresholding) and (GrayMapThreshold = 0) then begin
  2149.                 PutMessage(' Threshold must be non-zero.');
  2150.                 exit(SetupAutoOutline);
  2151.             end;
  2152.         if not MakingLOI then
  2153.             ShowWatch;
  2154.         SetupAutoOutline := true;
  2155.     end;
  2156.  
  2157.  
  2158.     procedure AutoOutline (start: point);
  2159.         var
  2160.             hloc, vloc: integer;
  2161.             TouchingEdge, BinaryPixel: boolean;
  2162.             direction: char;
  2163.             count: LongInt;
  2164.             Perimeter, CalibratedPerimeter, AspectRatio: real;
  2165.     begin
  2166.         with start do
  2167.             BinaryPixel := (MyGetPixel(h, v) = WhiteIndex) or (MyGetPixel(h, v) = BlackIndex);
  2168.         if not SetupAutoOutline(BinaryPixel) then
  2169.             exit(AutoOutline);
  2170.         if SelectionMode = NewSelection then
  2171.             KillRoi;
  2172.         with info^ do begin
  2173.                 with start do
  2174.                     if PixelInside(h, v) then begin
  2175.                             repeat
  2176.                                 h := h + 1;
  2177.                             until not PixelInside(h, v) or (h >= PicRect.right);
  2178.                             if not PixelInside(h - 1, v - 1) then
  2179.                                 direction := 'R'
  2180.                             else if PixelInside(h, v - 1) then
  2181.                                 direction := 'L'
  2182.                             else
  2183.                                 direction := 'D';
  2184.                         end
  2185.                     else begin
  2186.                             repeat
  2187.                                 h := h + 1;
  2188.                             until PixelInside(h, v) or (h >= PicRect.right);
  2189.                             direction := 'U';
  2190.                         end;
  2191.                 if start.h >= PicRect.right then begin
  2192.                         beep;
  2193.                         exit(AutoOutline);
  2194.                     end;
  2195.                 if TraceEdge(start.h, start.v, direction, TouchingEdge) then begin
  2196.                         WhatToUndo := NothingToUndo;
  2197.                         if WandAutoMeasure and not MakingLOI then begin
  2198.                                 GetHistogram;
  2199.                                 ComputeResults;
  2200.                                 if WandAdjustAreas then begin
  2201.                                         GetLengthOrPerimeter(Perimeter, CalibratedPerimeter);
  2202.                                         with RoiRect do
  2203.                                             AspectRatio := (right - left) / (bottom - top);
  2204.                                         count := PixelCount^[mCount] + round(Perimeter / 2.0 + AspectRatio * 1.5);
  2205.                                         PixelCount^[mCount] := count;
  2206.                                         if SpatiallyCalibrated then
  2207.                                             mArea^[mCount] := count / (xSpatialScale * ySpatialScale)
  2208.                                         else
  2209.                                             mArea^[mCount] := count;
  2210.                                     end;
  2211.                                 ShowValues;
  2212.                                 AppendResults;
  2213.                                 WhatToUndo := UndoMeasurement;
  2214.                                 if LabelParticles then
  2215.                                     MarkSelection(mCount);
  2216.                             end;
  2217.                         if not (WandAutoMeasure and LabelParticles) then
  2218.                             RoiShowing := true;
  2219.                         if not MakingLOI then
  2220.                             UpdateScreen(RoiRect);
  2221.                         if not WandAutoMeasure then
  2222.                             measuring := false;
  2223.                     end; {if}
  2224.             end; {with info}
  2225.     end;
  2226.  
  2227.  
  2228.     procedure RedoMeasurement;
  2229.         var
  2230.             SaveN, temp: integer;
  2231.             Canceled: boolean;
  2232.     begin
  2233.         if not isSelectionTool then begin
  2234.                 CurrentTool := SelectionTool;
  2235.                 isSelectionTool := true;
  2236.                 DrawTools;
  2237.             end;
  2238.         temp := GetInt('Measurement to Redo:', mCount, Canceled);
  2239.         if canceled then
  2240.             exit(RedoMeasurement);
  2241.         MeasurementToRedo := temp;
  2242.         if (MeasurementToRedo >= 1) and (MeasurementToRedo <= mCount) then begin
  2243.                 SaveN := mCount;
  2244.                 mCount := MeasurementToRedo;
  2245.                 ShowValues;
  2246.                 mCount := SaveN;
  2247.             end
  2248.         else begin
  2249.                 beep;
  2250.                 MeasurementToRedo := 0;
  2251.             end;
  2252.     end;
  2253.  
  2254.  
  2255.     procedure DeleteMeasurement;
  2256.         var
  2257.             nToDelete, i: integer;
  2258.             Canceled: boolean;
  2259.     begin
  2260.         nToDelete := GetInt('Measurement to delete:', mCount, Canceled);
  2261.         if (nToDelete >= 1) and (nToDelete <= mCount) and not Canceled then begin
  2262.                 for i := nToDelete to mCount - 1 do begin
  2263.                         mean^[i] := mean^[i + 1];
  2264.                         sd^[i] := sd^[i + 1];
  2265.                         PixelCount^[i] := PixelCount^[i + 1];
  2266.                         mArea^[i] := mArea^[i + 1];
  2267.                         mode^[i] := mode^[i + 1];
  2268.                         IntegratedDensity^[i] := IntegratedDensity^[i + 1];
  2269.                         idBackground^[i] := idBackground^[i + 1];
  2270.                         xcenter^[i] := xcenter^[i + 1];
  2271.                         ycenter^[i] := ycenter^[i + 1];
  2272.                         MajorAxis^[i] := MajorAxis^[i + 1];
  2273.                         MinorAxis^[i] := MinorAxis^[i + 1];
  2274.                         orientation^[i] := orientation^[i + 1];
  2275.                         mMin^[i] := mMin^[i + 1];
  2276.                         mMax^[i] := mMax^[i + 1];
  2277.                         plength^[i] := plength^[i + 1];
  2278.                     end; {for}
  2279.                 mCount := mCount - 1;
  2280.                 if mCount = 0 then begin
  2281.                         UnsavedResults := false;
  2282.                         beep;
  2283.                     end;
  2284.                 UpdateList;
  2285.             end
  2286.         else if not Canceled then
  2287.             beep;
  2288.     end;
  2289.  
  2290.  
  2291.     function DoAPDialog: boolean;
  2292.         const
  2293.             MinID = 6;
  2294.             MaxID = 7;
  2295.             LabelID = 8;
  2296.             OutlineID = 9;
  2297.             IgnoreID = 10;
  2298.             IncludeHolesID = 11;
  2299.             ResetID = 12;
  2300.         var
  2301.             mylog: DialogPtr;
  2302.             item: integer;
  2303.     begin
  2304.         InitCursor;
  2305.         mylog := GetNewDialog(220, nil, pointer(-1));
  2306.         SetDNum(MyLog, MinID, MinParticleSize);
  2307.         SetDNum(MyLog, MaxID, MaxParticleSize);
  2308.         SetDialogItem(mylog, IgnoreID, ord(IgnoreParticlesTouchingEdge));
  2309.         SetDialogItem(mylog, LabelID, ord(LabelParticles));
  2310.         SetDialogItem(mylog, OutlineID, ord(OutlineParticles));
  2311.         SetDialogItem(mylog, IncludeHolesID, ord(IncludeHoles));
  2312.         SetDialogItem(mylog, ResetID, ord(APReset));
  2313.         repeat
  2314.             ModalDialog(nil, item);
  2315.             if item = MinID then
  2316.                 MinParticleSize := GetDNum(MyLog, MinID);
  2317.             if item = MaxID then
  2318.                 MaxParticleSize := GetDNum(MyLog, MaxID);
  2319.             if item = IgnoreID then begin
  2320.                     IgnoreParticlesTouchingEdge := not IgnoreParticlesTouchingEdge;
  2321.                     SetDialogItem(mylog, IgnoreID, ord(IgnoreParticlesTouchingEdge));
  2322.                 end;
  2323.             if item = LabelID then begin
  2324.                     LabelParticles := not LabelParticles;
  2325.                     SetDialogItem(mylog, LabelID, ord(LabelParticles));
  2326.                 end;
  2327.             if item = OutlineID then begin
  2328.                     OutlineParticles := not OutlineParticles;
  2329.                     SetDialogItem(mylog, OutlineID, ord(OutlineParticles));
  2330.                 end;
  2331.             if item = IncludeHolesID then begin
  2332.                     IncludeHoles := not IncludeHoles;
  2333.                     SetDialogItem(mylog, IncludeHolesID, ord(IncludeHoles));
  2334.                 end;
  2335.             if item = ResetID then begin
  2336.                     APReset := not APReset;
  2337.                     SetDialogItem(mylog, ResetID, ord(APReset));
  2338.                 end;
  2339.         until (item = ok) or (item = cancel);
  2340.         DisposDialog(mylog);
  2341.         if MinParticleSize < 1 then
  2342.             MinParticleSize := 1;
  2343.         if MaxParticleSize > 999999 then
  2344.             MinParticleSize := 999999;
  2345.         if MaxParticleSize <= MinParticleSize then begin
  2346.                 MinParticleSize := 1;
  2347.                 MaxParticleSize := 999999;
  2348.             end;
  2349.         DoAPDialog := item <> cancel;
  2350.     end;
  2351.  
  2352.  
  2353.     procedure AnalyzeParticles;
  2354.         var
  2355.             hloc, vloc, AlertID, index, MaxTriesPerLine, nParticles: integer;
  2356.             SaveSliceState, TouchingEdge, DrawOutlines, AutoSelectAll, finished, OutsideSelection: boolean;
  2357.             SaveForegroundIndex, SaveBackgroundIndex, EraseIndex, OutlineIndex: integer;
  2358.             tPort: GrafPtr;
  2359.             ScanRect: rect;
  2360.             side: (TopSide, RightSide, BottomSide, LeftSide);
  2361.             dstRgn: rgnHandle;
  2362.  
  2363.         function PixelInside: boolean;
  2364.             var
  2365.                 value: integer;
  2366.                 offset: LongInt;
  2367.                 p: ptr;
  2368.         begin
  2369.             with info^ do begin {MyGetPixel inlined to speed things up.}
  2370.                     offset := LongInt(vloc) * BytesPerRow + hloc;
  2371.                     p := ptr(ord4(PicBaseAddr) + offset);
  2372.                 end;
  2373.             value := BAND(p^, 255);
  2374.             case ThresholdingMode of
  2375.                 DensitySlice: 
  2376.                     PixelInside := (value >= SliceStart) and (value <= SliceEnd);
  2377.                 GrayMapThresholding: 
  2378.                     PixelInside := value >= GrayMapThreshold;
  2379.                 BinaryImage: 
  2380.                     PixelInside := value = BlackIndex;
  2381.             end;
  2382.         end;
  2383.  
  2384.         procedure LabelBlobs;
  2385.             var
  2386.                 i: integer;
  2387.         begin
  2388.             if nParticles <= MaxMeasurements then
  2389.                 for i := 1 to mCount do begin
  2390.                         MarkSelection(i);
  2391.                         if i mod 50 = 0 then
  2392.                             UpdatePicWindow;
  2393.                         if CommandPeriod then begin
  2394.                                 beep;
  2395.                                 leave;
  2396.                             end;
  2397.                     end;
  2398.         end;
  2399.  
  2400.     begin
  2401.         with info^ do begin
  2402.                 if NotInBounds or NoUndo then
  2403.                     exit(AnalyzeParticles);
  2404.                 if not SetupAutoOutline(false) then
  2405.                     exit(AnalyzeParticles);
  2406.                 if RedirectSampling then begin
  2407.                         SetupRedirectedSampling;
  2408.                         if InfoForRedirect = nil then
  2409.                             exit(AnalyzeParticles)
  2410.                     end;
  2411.                 if not macro and not OptionKeyWasDown then
  2412.                     if not DoAPDialog then
  2413.                         exit(AnalyzeParticles);
  2414.                 AutoSelectAll := not RoiShowing;
  2415.                 if AutoSelectAll then
  2416.                     SelectAll(false);
  2417.                 ScanRect := RoiRect;
  2418.                 if not AutoSelectAll then
  2419.                     with ScanRect do begin
  2420.                             left := picrect.left;
  2421.                             right := PicRect.right;
  2422.                         end;
  2423.                 KillRoi;
  2424.                 if APReset then begin
  2425.                         ResetCounter;
  2426.                         if mCount > 0 then
  2427.                             exit(AnalyzeParticles);
  2428.                     end;
  2429.                 UpdatePicWindow;
  2430.                 SetupUndoFromClip;
  2431.                 SaveSliceState := DensitySlicing;
  2432.                 SaveForegroundIndex := ForegroundIndex;
  2433.                 SaveBackgroundIndex := BackgroundIndex;
  2434.                 SetForegroundColor(WhiteIndex);
  2435.                 DensitySlicing := false;
  2436.                 DrawOutlines := false;
  2437.                 case ThresholdingMode of
  2438.                     DensitySlice:  begin
  2439.                             EraseIndex := SliceStart - 1;
  2440.                             if EraseIndex < 0 then
  2441.                                 EraseIndex := WhiteIndex;
  2442.                             DrawOutlines := OutlineParticles;
  2443.                             OutLineIndex := BlackIndex;
  2444.                         end;
  2445.                     GrayMapThresholding:  begin
  2446.                             EraseIndex := GrayMapThreshold - 1;
  2447.                             if EraseIndex < 0 then
  2448.                                 EraseIndex := WhiteIndex;
  2449.                         end;
  2450.                     BinaryImage:  begin
  2451.                             DrawOutlines := OutlineParticles;
  2452.                             OutLineIndex := 254;
  2453.                             EraseIndex := 128;
  2454.                         end;
  2455.                 end;
  2456.                 AnalyzingParticles := true;
  2457.                 nParticles := 0;
  2458.                 GetPort(tPort);
  2459.                 SetPort(GrafPtr(osPort));
  2460.                 dstRgn := NewRgn;
  2461.                 SelectionMode := NewSelection;
  2462.                 ShowWatch;
  2463.                 with ScanRect do
  2464.                     for vloc := top to bottom - 1 do
  2465.                         for hloc := left to right - 1 do begin
  2466.                                 if PixelInside then begin
  2467.                                         if TraceEdge(hloc, vloc, 'U', TouchingEdge) then begin
  2468.                                                 nParticles := nParticles + 1;
  2469.                                                 RoiShowing := false;
  2470.                                                 if mCount < MaxMeasurements then begin
  2471.                                                         GetHistogram;
  2472.                                                         ComputeResults;
  2473.                                                     end;
  2474.                                                 SetBackgroundColor(EraseIndex);
  2475.                                                 EraseRgn(roiRgn);
  2476.                                                 if AutoSelectAll then
  2477.                                                     OutSideSelection := false
  2478.                                                 else begin
  2479.                                                         SectRgn(roiRgn, NoInfo^.RoiRgn, dstRgn);
  2480.                                                         OutSideSelection := EmptyRgn(dstRgn);
  2481.                                                     end;
  2482.                                                 if (PixelCount^[mCount] < MinParticleSize) or (PixelCount^[mCount] > MaxParticleSize) or TouchingEdge or OutsideSelection then begin
  2483.                                                         mCount := mCount - 1;
  2484.                                                         nParticles := nParticles - 1;
  2485.                                                         UpdateScreen(RoiRect);
  2486.                                                     end
  2487.                                                 else begin
  2488.                                                         if DrawOutlines then begin
  2489.                                                                 SetForegroundColor(OutlineIndex);
  2490.                                                                 FrameRgn(roiRgn);
  2491.                                                             end;
  2492.                                                         UpdateScreen(RoiRect);
  2493.                                                         if nParticles <= MaxMeasurements then begin
  2494.                                                                 ShowValues;
  2495.                                                                 AppendResults;
  2496.                                                             end
  2497.                                                         else
  2498.                                                             ShowMessage(long2str(nParticles));
  2499.                                                         if nParticles = MaxMeasurements then
  2500.                                                             beep;
  2501.                                                         if CommandPeriod or (AnalyzingParticles = false) then begin {quit}
  2502.                                                                 beep;
  2503.                                                                 SetPort(tPort);
  2504.                                                                 if LabelParticles then
  2505.                                                                     LabelBlobs;
  2506.                                                                 DensitySlicing := SaveSliceState;
  2507.                                                                 SetForegroundColor(SaveForegroundIndex);
  2508.                                                                 SetBackgroundColor(SaveBackgroundIndex);
  2509.                                                                 KillRoi;
  2510.                                                                 UpdatePicWindow;
  2511.                                                                 WhatToUndo := UndoEdit;
  2512.                                                                 UndoFromClip := true;
  2513.                                                                 AnalyzingParticles := false;
  2514.                                                                 DisposeRgn(dstRgn);
  2515.                                                                 exit(AnalyzeParticles);
  2516.                                                             end; {quit}
  2517.                                                     end;
  2518.                                             end;  {if TraceEdge}
  2519.                                     end; {if PixelInside}
  2520.                             end; {for}
  2521.             end; {with}
  2522.         SetPort(tPort);
  2523.         if LabelParticles then
  2524.             LabelBlobs;
  2525.         DensitySlicing := SaveSliceState;
  2526.         SetForegroundColor(SaveForegroundIndex);
  2527.         SetBackgroundColor(SaveBackgroundIndex);
  2528.         KillRoi;
  2529.         UpdatePicWindow;
  2530.         if ThresholdingMode = GrayMapThresholding then
  2531.             ResetGrayMap;
  2532.         WhatToUndo := UndoEdit;
  2533.         UndoFromClip := true;
  2534.         AnalyzingParticles := false;
  2535.         DisposeRgn(dstRgn);
  2536.     end;
  2537.  
  2538.  
  2539.     procedure MakeNonStraightLineRoi (RoiKind: RoiTypeType);
  2540.         var
  2541.             i, ff: integer;
  2542.             SaveInfo: InfoPtr;
  2543.             pt, spt, start: point;
  2544.     begin
  2545.         SetupUndoInfoRec;
  2546.         SaveInfo := Info;
  2547.         Info := UndoInfo;
  2548.         with info^ do begin
  2549.                 magnification := SaveInfo^.magnification;
  2550.                 SrcRect := SaveInfo^.SrcRect;
  2551.                 BinaryPic := true;
  2552.                 SetPort(GrafPtr(osPort));
  2553.             end;
  2554.         pmForeColor(BlackIndex);
  2555.         pmBackColor(WhiteIndex);
  2556.         PenNormal;
  2557.         PenSize(LineWidth, LineWidth);
  2558.         EraseRect(info^.PicRect);
  2559.         ff := LineWidth div 2;
  2560.         if ff < 0 then
  2561.             ff := 0;
  2562.         MakingLOI := true;
  2563.         ConvertCoordinates;
  2564.         spt.h := xCoordinates^[1];
  2565.         spt.v := yCoordinates^[1];
  2566.         MoveTo(spt.h - ff, spt.v - ff);
  2567.         for i := 2 to nCoordinates do begin
  2568.                 pt.h := xCoordinates^[i];
  2569.                 pt.v := yCoordinates^[i];
  2570.                 LineTo(pt.h - ff, pt.v - ff);
  2571.             end;
  2572.         start := spt;
  2573.         start.h := start.h - 1;
  2574.         AutoOutline(start);
  2575.         MakingLOI := false;
  2576.         info^.RoiShowing := false;
  2577.         Info := SaveInfo;
  2578.         with info^ do begin
  2579.                 CopyRgn(UndoInfo^.roiRgn, roiRgn);
  2580.                 RoiRect := UndoInfo^.RoiRect;
  2581.                 SetEmptyRgn(UndoInfo^.roiRgn);
  2582.                 RoiShowing := true;
  2583.                 SetupUndo;
  2584.                 roiType := RoiKind;
  2585.                 with RoiRect do begin
  2586.                         LX1 := spt.h - left;
  2587.                         LY1 := spt.v - top;
  2588.                         LX2 := pt.h - left;
  2589.                         LY2 := pt.v - top;
  2590.                     end;
  2591.             end; {with info^}
  2592.         MakeCoordinatesRelative;
  2593.     end;
  2594.  
  2595.  
  2596. end.